在.NET中隐藏inheritance的通用接口成员:好,坏还是难看?

我知道类实现中的阴影成员可能导致“错误”成员可以被调用的情况,这取决于我如何构建我的实例,但是对于接口我没有看到这可能是一个问题,我发现自己编写接口像这样经常这样:

public interface INode { IEnumerable Children { get; } } public interface INode : INode where N : INode { new IEnumerable Children { get; } } public interface IAlpha : INode { } public interface IBeta : INode { } 

我的代码中有一些地方只知道INode所以孩子们也应该是INode类型。

在其他地方我想了解具体的类型 – 在我的示例IAlphaIBeta接口的实现中,我希望子类型与其父类型相同。

所以我实现了一个NodeBase类:

 public abstract class NodeBase : INode where N : INode { protected readonly List _children = new List(); public IEnumerable Children { get { return _children.AsEnumerable(); } } IEnumerable INode.Children { get { return this.Children.Cast(); } } } 

实际实现中没有阴影,仅在接口中。

IAlphaIBeta具体实例如下所示:

 public class Alpha : NodeBase, IAlpha { IEnumerable INode.Children { get { return this.Children.Cast(); } } } public class Beta : NodeBase, IBeta { IEnumerable INode.Children { get { return this.Children.Cast(); } } } 

同样,在实现中没有阴影。

我现在可以像这样访问这些类型:

 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; var betaAsINodeBeta = beta as INode; var alphaAsINodeIAlpha = alpha as INode; var betaAsINodeIBeta = beta as INode; var alphaAsNodeBaseAlpha = alpha as NodeBase; var betaAsNodeBaseBeta = beta as NodeBase; 

这些变量中的每一个现在都具有正确的强类型Children集合。

所以,我的问题很简单。 接口成员的阴影是否使用这种模式好,坏或难看? 为什么?

我会说你自己有一个相当复杂的场景,我通常会尝试让事情变得简单 – 但是如果它适合你,我认为可以添加更多这样的信息。 (在你到达IAlphaIBeta位之前似乎是合理的;没有这些接口, AlphaBeta根本不需要任何实现,并且调用者可以只使用INodeINode

特别要注意的是IEnumerable实际上做了同样的事情 – 不可否认,将一个generics隐藏在另一个generics中,但隐藏了非generics的generics。

其他四点:

  • 你在NodeBaseAsEnumerable调用毫无意义; 调用者仍然可以转换为List 。 如果你想阻止它,你可以做一些像Select(x => x)事情。 (理论上Skip(0) 可能会起作用,但它可以被优化掉; LINQ to Objects在保证隐藏原始实现的运算符方面并没有很好地记录。 Select保证不会。实际上, Take(int.MaxValue)也可以。)

  • 从C#4开始,由于协方差,您的两个“叶子”类可以简化:

     public class Alpha : NodeBase, IAlpha { IEnumerable INode.Children { get { return Children; } } } public class Beta : NodeBase, IBeta { IEnumerable INode.Children { get { return Children; } } } 
  • 从C#4开始, 如果您愿意将N限制为引用类型, INode.Children可以简化NodeBase实现:

     public abstract class NodeBase : INode where N : class, INode // Note the class constraint { ... IEnumerable INode.Children { get { return this.Children; } } } 
  • 从C#4开始,您可以声明INodeN是协变的:

     public interface INode : INode 

为什么不简单地使用类型参数(这是generics类型的参数)来确定子类型。 然后INode仍然会有相同的sematics,但你根本不需要阴影而你确实在实现中有阴影到INode会导致你在post中描述的相同问题