Shadowing Inherited Generic Interface Members in .NET: good, bad or ugly?

天大地大妈咪最大 提交于 2019-12-04 02:39:28

I would say you've got yourself a pretty complicated scenario there, and I generally try to keep things simpler than that - but if it works for you, I think it's okay to add more information like this. (It seems reasonable until you get to the IAlpha and IBeta bit; without those interfaces, Alpha and Beta don't need any implementation at all, and callers can just use INode<IAlpha> and INode<IBeta> instead.

In particular, note that IEnumerable<T> effectively does the same thing - not hiding one generic with another, admittedly, but hiding a non-generic with a generic.

Four other points:

  • Your call to AsEnumerable in NodeBase is pointless; callers can still cast to List<T>. If you want to prevent that, you can do something like Select(x => x). (In theory Skip(0) might work, but it could be optimized away; LINQ to Objects isn't terribly well documented in terms of which operators are guaranteed to hide the original implementation. Select is guaranteed not to. Realistically, Take(int.MaxValue) would work too.)

  • As of C# 4, your two "leaf" classes can be simplified due to covariance:

    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; } }
    }
    
  • As of C# 4, your NodeBase implementation of INode.Children can be simplified if you're willing to restrict N to be a reference type:

    public abstract class NodeBase<N> : INode<N>
        where N : class, INode<N> // Note the class constraint
    {
        ...
    
        IEnumerable<INode> INode.Children
        {
            get { return this.Children; }
        }
    }
    
  • As of C# 4, you can declare INode<N> to be covariant in N:

    public interface INode<out N> : INode
    

Why not simply use a type argument (that is an argument to the generic type) to determine the type of a child. Then INode would still have the same sematics but you wouldn't need to shadow at all And you do indeed have shadowing in the implementation casting to INode will result in the same issues you describe in your post

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!