2011-08-12 38 views
9

我知道類實現中的shadowing成員可能會導致出現「錯誤」成員可以調用的情況,這取決於我如何投射我的實例,但使用接口我沒有看到這可能是一個問題,我發現我自己寫接口這樣往往:隱藏.NET中的繼承通用接口成員:好,壞或醜?

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集合。

所以,我的問題很簡單。界面成員使用這種模式的陰影是好的,壞的還是醜陋的?爲什麼?

+0

爲什麼不簡單地使用類型參數來確定一個孩子的類型。然後INode 仍然有相同的語法,但你根本不需要影子 –

+0

@Rune - 你是什麼意思的「類型參數」? – Enigmativity

+0

@Enigmativity:我編輯了我的答案,以簡化NodeBase的INode.Children實現 - 我已經找出了我之前缺少的東西。 –

回答

8

我想說你已經有了一個非常複雜的場景,我通常會嘗試來讓事情變得簡單 - 但如果它適合你,我認爲可以添加更多信息。 (直到你到了IAlphaIBeta位似乎合理的;如果沒有這些接口,AlphaBeta不需要任何實現的一切,呼叫者可以只使用INode<IAlpha>INode<IBeta>代替

尤其注意IEnumerable<T>。有效地做同樣的事情 - 而不是躲在一個通用的另一個不可否認的,但隱藏非通用與通用

四,其他幾點:

  • 您致電在NodeBase是毫無意義的;來電者仍然可以投射到List<T>。如果你想防止這種情況發生,你可以像Select(x => x)那樣做。 (理論上Skip(0)威力工作,但它可能被優化掉; LINQ到對象是不是非常好,其中的運營商,保證隱藏原始實施方面已記錄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 
    
+0

謝謝你的另一個很好的答案。關於'AsEnumerable'被拋回到'List '這一點扔給了我。在Rx中,'AsObservable'方法**不會隱藏源觀察值。我只是假設'AsEnumerable'也是這樣。也許Rx傢伙很簡單。 – Enigmativity

0

爲什麼不能簡單地用一個類型參數(即參數傳遞給泛型類型),以確定孩子的類型。然後,INode仍然具有相同的Sematics,但是根本不需要影子 而且確實在實施中投影到INode會導致與您在帖子中描述的問題相同的問題