Eni*_*ity 9 .net c# oop generics shadowing
我知道类实现中的阴影成员会导致"错误"成员可以被调用的情况,这取决于我如何构建我的实例,但是对于接口我没有看到这可能是一个问题,我发现自己编写接口像这样经常这样:
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>
{ }
Run Code Online (Sandbox Code Playgroud)
我的代码中只有一些地方知道,INode所以孩子也应该是类型INode.
在其他地方,我想了解具体的类型 - 在我的示例IAlpha和IBeta接口的实现中,我希望子类型与其父类型相同.
所以我实现了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>(); }
}
}
Run Code Online (Sandbox Code Playgroud)
实际实现中没有阴影,仅在接口中.
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>(); }
}
}
Run Code Online (Sandbox Code Playgroud)
同样,在实现中没有阴影.
我现在可以像这样访问这些类型:
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>;
Run Code Online (Sandbox Code Playgroud)
这些变量中的每一个现在都具有正确的强类型Children集合.
所以,我的问题很简单.界面成员的阴影是否使用这种模式好,坏或难看?为什么?
我会说你在那里遇到了一个相当复杂的场景,我通常会尝试让事情变得简单 - 但如果它适合你,我认为可以添加更多这样的信息.(直到你到达这似乎是合理的IAlpha,并IBeta没有这些接口;位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; } }
}
Run Code Online (Sandbox Code Playgroud)从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; }
}
}
Run Code Online (Sandbox Code Playgroud)从C#4开始,您可以声明INode<N>在以下方面是协变的N:
public interface INode<out N> : INode
Run Code Online (Sandbox Code Playgroud)