传入类型的类型安全性

时间:2018-04-14 00:05:52

标签: c#

目前我正在对代码进行单元测试后重构代码,并且从设计的角度考虑类型安全方面的重构问题。我的原始代码看起来有点像这样:

接口

public interface IBase
{
    int ID { get; set; }
}

public interface IFirstSub : IBase
{
    string Description { get; set; }
}

public interface ISecondSub : IBase
{
    decimal Total { get; set; }
}

public interface IThirdSub : IBase
{
    int Count { get; set; }
}

public interface IBaseContainer
{
    void Add(IBase baseParam);
}

实现

public class FirstContainer : IBaseContainer
{
    public void Add(IBase baseParam)
    {
        if (!(baseParam is IFirstSub || baseParam is ISecondSub))
        {
            throw new ArgumentException(nameof(baseParam));
        }

        // Do Something
    }
}

public class SecondContainer : IBaseContainer
{
    public void Add(IBase baseParam)
    {
        if (!(baseParam is IThirdSub))
        {
            throw new ArgumentException(nameof(baseParam));
        }

        // Do Something
    }
}

使用FirstContainerSecondContainer的原始实现,它在Add方法的开头重复相同的逻辑,所以我认为我会重构代码以查找某些内容< em>喜欢这个:

public abstract class BaseContainer : IBaseContainer
{
    private readonly List<Type> _types = new List<Type>();

    protected BaseContainer(params Type[] baseTypes)
    {
        _types.AddRange(baseTypes);
    }

    public void Add(IBase baseParam)
    {
        if (_types.All(type => !type.IsInstanceOfType(baseParam)))
        {
            throw new ArgumentException(nameof(baseParam));
        }

        DoSomething(baseParam);
    }

    protected abstract void DoSomething(IBase baseParam);
}

public class ThirdContainer : BaseContainer
{
    public ThirdContainer() : base(typeof(IFirstSub)) { }

    protected override void DoSomething(IBase baseParam)
    {
        // Do Something
    }
}

完成这个重构后,它成功地从Add方法的开头删除了代码的重复,但我对重构的主要关注是调用基础构造函数base(typeof(IFirstSub))不是真正的类型安全。通过这个,我的意思是我可以像base(typeof(object))那样调用基础构造函数,它将编译。出于我的项目的目的,我想将类型约束为继承IBase的类型,并在编译时强制执行。

无论如何都要克服这个限制,还是需要新的设计来实现这个目标?

1 个答案:

答案 0 :(得分:4)

不,它不是类型安全的

在运行时传递和验证类​​型不是类型安全的,因为类型安全是编译时的概念。在我看来,你的重构努力并没有改进代码,实际上做了一些非常奇怪的事情。

功能重载

如果您需要一种接受两种类型之一的方法,可以使用function overloading

public class FirstContainer : IBaseContainer
{
    public void Add(IFirstSub param)
    {
        // Do Something
    }
    public void Add(ISecondSub param)
    {
        // Do Something
    }
}

编译器会自动为您选择正确的原型,并且不允许IFirstSubISecondSub以外的其他任何内容。

创建另一个界面

另一种方法要求您为具有共同点的类型添加接口,如下所示:

interface ICanBeHeldInFirstContainer
{ }

public interface IFirstSub : IBase, ICanBeHeldInFirstContainer
{
    string Description { get; set; }
}

public interface ISecondSub : IBase, ICanBeHeldInFirstContainer
{
    decimal Total { get; set; }
}

然后你这样做:

public class FirstContainer : IBaseContainer
{
    public void Add(ICanBeHeldInFirstContainer param)
    {
        // Do Something
    }
}

或者这个:

public class FirstContainer : IBaseContainer
{
    public void Add<T>(T param) where T : ICanBeHeldInFirstContainer 
    {
        // Do Something
    }
}
相关问题