Я знаю, что затенение членов в реализациях классов может привести к ситуациям, когда «неправильный» член может быть вызван в зависимости от того, как я привел свои экземпляры, но с интерфейсами я не вижу, что это может быть проблемой, и я обнаружил, что пишу интерфейсы так довольно часто:
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
, поэтому дочерние элементы также должны относиться к типу 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>(); }
}
}
Никакого затенения в реальной реализации, только в интерфейсах.
Конкретные экземпляры 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>(); }
}
}
Опять же, никакого затенения в реализациях.
Теперь я могу получить доступ к этим типам следующим образом:
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
.
Итак, мои вопросы просты. Является ли затенение элементов интерфейса с использованием такого шаблона хорошим, плохим или уродливым? И почему?