c# - 在 .NET : good, 中隐藏继承的通用接口(interface)成员是坏的还是丑的?

标签 c# .net oop generics shadowing

我知道在类实现中隐藏成员可能会导致调用“错误”成员的情况,具体取决于我如何转换我的实例,但是对于接口(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 体的类型——在我的示例 IAlphaIBeta 接口(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)中。

IAlphaIBeta 的具体实例如下所示:

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)成员的阴影是好是坏还是丑陋?为什么?

最佳答案

我会说你遇到了一个相当复杂的场景,我通常尝试让事情变得比这更简单 - 但如果它适合你,我认为添加更多信息是可以的像这样。 (这似乎是合理的,直到你到达 IAlphaIBeta 位;没有这些接口(interface),AlphaBeta 根本不需要任何实现,调用者可以只使用 INode<IAlpha>INode<IBeta>相反。

特别注意 IEnumerable<T>有效地做同样的事情 - 诚然,不是将一个泛型与另一个隐藏在一起,而是将一个非泛型与一个泛型隐藏起来。

其他四点:

  • 您调用 AsEnumerableNodeBase毫无意义;来电者仍然可以转换为 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/

相关文章:

javascript - 如何在 JavaScript 中使用静态方法中的私有(private)成员?

c# - 依赖注入(inject)可选参数

c# - 如何创建一个简单的本地 SQL 数据库并使用 C# 向其中插入值?

c# - 从 byte[] 中提取可变宽度有符号整数的最快方法

c# - 调整了循环目录的输出

.net - 创建应用程序包时出错

c# - C# 中的高斯拟合

.net - .NET 中的 CaSTLe、AOP 和日志记录

oop - OOA中的类(class)设计

Python 类 - 使用实例作为属性