目前我正在对代码进行单元测试后重构代码,并且从设计的角度考虑类型安全方面的重构问题。我的原始代码看起来有点像这样:
接口
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
}
}
使用FirstContainer
和SecondContainer
的原始实现,它在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
的类型,并在编译时强制执行。
无论如何都要克服这个限制,还是需要新的设计来实现这个目标?
答案 0 :(得分:4)
在运行时传递和验证类型不是类型安全的,因为类型安全是编译时的概念。在我看来,你的重构努力并没有改进代码,实际上做了一些非常奇怪的事情。
如果您需要一种接受两种类型之一的方法,可以使用function overloading:
public class FirstContainer : IBaseContainer
{
public void Add(IFirstSub param)
{
// Do Something
}
public void Add(ISecondSub param)
{
// Do Something
}
}
编译器会自动为您选择正确的原型,并且不允许IFirstSub
或ISecondSub
以外的其他任何内容。
另一种方法要求您为具有共同点的类型添加接口,如下所示:
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
}
}