C# 在.NET中隐藏继承的通用接口成员:好、坏还是难看?
我知道在类实现中隐藏成员可能会导致“错误”成员被调用的情况,具体取决于我如何转换实例,但对于接口,我不认为这是一个问题,我发现自己经常编写这样的接口:C# 在.NET中隐藏继承的通用接口成员:好、坏还是难看?,c#,.net,oop,generics,shadowing,C#,.net,Oop,Generics,Shadowing,我知道在类实现中隐藏成员可能会导致“错误”成员被调用的情况,具体取决于我如何转换实例,但对于接口,我不认为这是一个问题,我发现自己经常编写这样的接口: public interface INode { IEnumerable<INode> Children { get; } } public interface INode<N> : INode where N : INode<N> { new IEnumerable<N>
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>
{ }
在实际实现中没有阴影,只有在接口中
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>(); }
}
}
公共类Alpha:NodeBase,IAlpha
{
可数INode.儿童
{
获取{返回this.Children.Cast();}
}
}
公共类测试版:NodeBase,IBeta
{
可数INode.儿童
{
获取{返回this.Children.Cast();}
}
}
同样,在实现中没有阴影
我现在可以像这样访问这些类型:
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>(); }
}
}
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>;
var alpha=新alpha();
var beta=新的beta();
var alphasisalpha=α为IAlpha;
var betaAsIBeta=作为IBeta的beta;
var alphaAsINode=α作为索引节点;
var betaAsINode=βas INode;
var Alphasinodealpha=α为INode;
var betaAsINodeBeta=作为INode的β;
var alphaAsINodeIAlpha=α为INode;
var BetaAsinodeBeta=作为INode的beta;
var alphaasnodebaselpha=alpha作为NodeBase;
var betaAsNodeBaseBeta=作为节点基的beta;
现在,这些变量中的每一个都有正确的、强类型的子类
集合
所以,我的问题很简单。使用这种模式的接口成员的阴影是好的、坏的还是丑陋的?为什么呢?我想说的是,你有一个非常复杂的场景,我通常会尽量让事情简单一些——但如果它对你有效,我认为可以添加更多类似这样的信息。(在到达
IAlpha
和IBeta
位之前,这似乎是合理的;没有这些接口,Alpha
和Beta
根本不需要任何实现,调用方只需使用INode
和INode
)
特别要注意的是,IEnumerable
有效地做了同样的事情——不必承认,用一个泛型隐藏一个泛型,而是用一个泛型隐藏一个非泛型
其他四点:
- 在
中调用NodeBase
是毫无意义的;调用方仍然可以强制转换到AsEnumerable
。如果要防止这种情况发生,可以执行类似List
(理论上是Select(x=>x)
可能会起作用,但可以进行优化;LINQ to Objects并没有很好的文档说明哪些操作符可以保证隐藏原始实现。跳过(0)
肯定不会。实际上,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开始,您可以在
中将N
声明为协变:INode
public interface INode<out N> : INode
公共接口INode:INode
而且您确实在实现中有阴影,对INode的强制转换将导致与您在帖子中描述的问题相同的问题为什么不简单地使用类型参数来确定子节点的类型。这样INode仍然具有相同的语义,但您不需要在all@Rune-你所说的“类型参数”是什么意思?@Enigmativity:我编辑了我的答案,以简化NodeBase对INode的实现。Children-我已经解决了我以前缺少的东西。@Jon-谢谢。非常感谢。感谢你的另一个优秀答案。关于
AsEnumerable
可转换回列表的观点让我很困惑。在Rx中,AsObservable
方法不支持ide源是可观察的。我只是假设aseneumerable
也是如此。也许Rx的人就是很聪明。
public interface INode<out N> : INode