我知道在类实现中隐藏成员可能会导致调用“错误”成员的情况,具体取决于我如何转换我的实例,但是对于接口(interface)我不认为这可能是个问题而且我发现我自己经常编写这样的界面:
public interface INode
{
IEnumerable<INode> Children { get; }
}
public interface INode<N> : INode
where N : INode<N>
{
new IEnumerable<N> Children { get; }
}
public interface IAlpha : INode<IAlpha>
{ }
public interface IBeta : INode<IBeta>
{ }
我的代码中有一些地方只知道INode
,所以 child 也应该是INode
类型。
在其他地方我想知 Prop 体的类型——在我的示例 IAlpha
和 IBeta
接口(interface)的实现中我希望 children 的类型与他们的相同 parent 。
所以我实现了一个 NodeBase
类,如下所示:
public abstract class NodeBase<N> : INode<N>
where N : INode<N>
{
protected readonly List<N> _children = new List<N>();
public IEnumerable<N> Children
{
get { return _children.AsEnumerable(); }
}
IEnumerable<INode> INode.Children
{
get { return this.Children.Cast<INode>(); }
}
}
实际实现中没有阴影,仅在接口(interface)中。
IAlpha
和 IBeta
的具体实例如下所示:
public class Alpha : NodeBase<Alpha>, IAlpha
{
IEnumerable<IAlpha> INode<IAlpha>.Children
{
get { return this.Children.Cast<IAlpha>(); }
}
}
public class Beta : NodeBase<Beta>, IBeta
{
IEnumerable<IBeta> INode<IBeta>.Children
{
get { return this.Children.Cast<IBeta>(); }
}
}
同样,实现中没有阴影。
我现在可以像这样访问这些类型:
var alpha = new Alpha();
var beta = new Beta();
var alphaAsIAlpha = alpha as IAlpha;
var betaAsIBeta = beta as IBeta;
var alphaAsINode = alpha as INode;
var betaAsINode = beta as INode;
var alphaAsINodeAlpha = alpha as INode<Alpha>;
var betaAsINodeBeta = beta as INode<Beta>;
var alphaAsINodeIAlpha = alpha as INode<IAlpha>;
var betaAsINodeIBeta = beta as INode<IBeta>;
var alphaAsNodeBaseAlpha = alpha as NodeBase<Alpha>;
var betaAsNodeBaseBeta = beta as NodeBase<Beta>;
现在每个变量都有正确的强类型 Children
集合。
所以,我的问题很简单。使用这种模式的接口(interface)成员的阴影是好是坏还是丑陋?为什么?
最佳答案
我会说你遇到了一个相当复杂的场景,我通常尝试让事情变得比这更简单 - 但如果它适合你,我认为添加更多信息是可以的像这样。 (这似乎是合理的,直到你到达 IAlpha
和 IBeta
位;没有这些接口(interface),Alpha
和 Beta
根本不需要任何实现,调用者可以只使用 INode<IAlpha>
和 INode<IBeta>
相反。
特别注意 IEnumerable<T>
有效地做同样的事情 - 诚然,不是将一个泛型与另一个隐藏在一起,而是将一个非泛型与一个泛型隐藏起来。
其他四点:
您调用
AsEnumerable
在NodeBase
毫无意义;来电者仍然可以转换为List<T>
.如果你想阻止这种情况,你可以做类似Select(x => x)
的事情。 . (理论上Skip(0)
可能 有效,但它可以 被优化掉;LINQ to Objects 没有很好地记录哪些运算符可以保证隐藏原始实现。Select
保证不会。实际上,Take(int.MaxValue)
也可以。)从 C# 4 开始,由于协变性,您的两个“叶”类可以得到简化:
public class Alpha : NodeBase<Alpha>, IAlpha { IEnumerable<IAlpha> INode<IAlpha>.Children { get { return Children; } } } public class Beta : NodeBase<Beta>, IBeta { IEnumerable<IBeta> INode<IBeta>.Children { get { return Children; } } }
从 C# 4 开始,您的
NodeBase
实现INode.Children
如果您愿意限制N
可以简化成为引用类型:public abstract class NodeBase<N> : INode<N> where N : class, INode<N> // Note the class constraint { ... IEnumerable<INode> INode.Children { get { return this.Children; } } }
从 C# 4 开始,您可以声明
INode<N>
在N
中协变:public interface INode<out N> : INode
关于c# - 在 .NET : good, 中隐藏继承的通用接口(interface)成员是坏的还是丑的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7035781/