Wiem, że członkowie shadowingu w implementacjach klas mogą prowadzić do sytuacji, w których "niewłaściwy" członek może zostać wywołany w zależności od tego, w jaki sposób rzuciłem moje instancje, ale z interfejsami nie widzę, że może to stanowić problem i sam się znajduję pisanie interfejsy jak to dość często:Shadowing Inherited Generic Interface Członkowie w .NET: dobry, zły czy brzydki?
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>
{ }
mam miejsca w kodzie, które znają tylko o INode
więc dzieci powinny być również typu INode
.
W innych miejscach, chcę wiedzieć o konkretnych typów - w realizacji mojego przykładu IAlpha
& IBeta
interfejsów chcę dzieci mają być wpisane tak samo jak ich rodziców.
więc zaimplementować klasę NodeBase
tak:
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>(); }
}
}
Nie shadowing w rzeczywistej implementacji, tylko w interfejsach.
szczególne przypadki IAlpha
& IBeta
wyglądać następująco:
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>(); }
}
}
Znowu nie cieniowanie w realizacji.
mogę teraz dostęp do tych rodzajów tak:
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>;
każdej z tych zmiennych mają teraz poprawne, silnie typu Children
kolekcję.
Moje pytania są proste. Czy cieniowanie członków interfejsu przy użyciu tego rodzaju wzoru jest dobre, złe czy brzydkie? I dlaczego?
Dlaczego po prostu nie używać wpisz argument, aby określić typ dziecka. Wtedy INODA nadal miałaby te same cechy, ale nie musiałaby w ogóle cienia –
@Rune - Co rozumiesz przez "argument typu"? – Enigmativity
@Enigmativity: Edytowałem swoją odpowiedź, aby uprościć implementację INode.Children przez NodeBase - opracowałem, czego wcześniej brakowało. –