2011-03-09 64 views
6

我有一個父類容器可能含有任何種類的節點,這裏節點是一個泛型類的特定於母公司的子類,像這樣:通用子C#泛型父

public class ContainerBase<NodeType, ObjectType> 
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase { 
} 

public abstract class NodeBase<T> where T : ObjectBase { 
    ContainerBase<NodeBase<T>, T> container; 
    public NodeBase(ContainerBase<NodeBase<T>, T> owner) { 
     container = owner; 
    } 
} 

什麼我想要做的是創造簡單實現標準的對象類型的具體子類:

public class ContainerNormal : ContainerBase<NodeNormal, ObjectNormal> { 
} 

public class NodeNormal : NodeBase<ObjectNormal> { 
    //This doesn't work 
    public NodeNormal(ContainerNormal owner) : base(owner) { } 
} 

我有點明白,爲什麼在調用基類的構造不起作用。它試圖將ContainerNormal轉換爲ContainerBase<NodeBase<ObjectNormal>, ObjectNormal>,這實際上並不奏效。

那麼我錯過了什麼樣的設計模式來使這項工作正確?或者我只需要在構造函數中輸入ContainerBase<NodeBase<ObjectNormal>,ObjectNormal>即使它不一定是ContainerNormal對象?

回答

3

探索:Covariance and contravariance,但這應該工作:

public class Container<TNode, T> where TNode : Node<T> { } 

public abstract class Node<T> 
{ 
    Container<Node<T>, T> container; 

    public Node(Container<Node<T>, T> owner) 
    { 
     this.container = owner; 
    } 
} 

public class ContainerNormal<T> : Container<Node<T>, T> { } 

public class NodeNormal<T> : Node<T> 
{ 
    public NodeNormal(ContainerNormal<T> container) 
     : base(container) 
    { 
    } 
} 

public class ContainerNormal : ContainerNormal<string> { } 

public class NodeNormal : NodeNormal<string> 
{ 
    public NodeNormal(ContainerNormal container) 
     : base(container) 
    { 
    } 
} 
+0

這看起來像它可能實際上工作,雖然它最終會混亂命名空間有點... – 2011-03-09 19:54:11

+0

經過檢查,它似乎有同樣的問題,因爲可以Genger的答案,因爲試圖從ContainerNormal獲得NodeNormal將改爲返回節點,這幾乎是相同的,但不完全相同。 – 2011-03-09 20:00:34

1

在C#4你可以做到這一點使用通用接口協方差:

public class ContainerBase<NodeType, ObjectType> : IContainerBase<NodeType, ObjectType> 
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase { 
} 

public abstract class NodeBase<T> where T : ObjectBase { 
    IContainerBase<NodeBase<T>, T> container; 
    public NodeBase(IContainerBase<NodeBase<T>, T> owner) { 
     container = owner; 
    } 
} 

public class ContainerNormal : ContainerBase<NodeNormal, ObjectNormal> { 
} 

public interface IContainerBase<out NodeType, ObjectType> 
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase { 
} 

public class NodeNormal : NodeBase<ObjectNormal> { 
    //This doesn't work 
    public NodeNormal(ContainerNormal owner) : base(owner) { } 
} 

public class ObjectNormal : ObjectBase {} 

public class ObjectBase{} 

當然,這隻會工作,如果你IContainerBase接口可避免將採取NodeType作爲輸入的任何功能。例如,這會工作:

public interface IContainerBase<out NodeType, ObjectType> 
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase 
{ 
    NodeType NodeTypeProp {get;} 
} 

...但這不會:

public interface IContainerBase<out NodeType, ObjectType> 
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase 
{ 
    NodeType NodeTypeProp {get;set;} // "set" not allowed on "out" type 
} 

還要說明一點:你注意到了我如何命名屬性「NodeTypeProp」,而不是「節點類型「?這是因爲我們沒有遵循C#命名約定。你應該用 「T」 字頭通用型名稱:

public interface IContainerBase<out TNodeType, TObjectType> 
    where TNodeType : NodeBase<TObjectType> where TObjectType : ObjectBase 
+0

不幸的是,我被困在.NET 3.5,否則這聽起來像最乾淨的解決方案。 – 2011-03-09 19:53:03

+0

@Ed馬蒂:這太遺憾了。過去我必須做的就是將所有這些課程緊密結合在一起。所以NodeBase的泛型參數將包括ContainerBase類型,而ContainerBase的泛型參數將包含NodeBase類型。它很醜。 – StriplingWarrior 2011-03-09 20:46:07

0

您可以可以改變集裝箱的正常的定義如下:

public class ContainerNormal : ContainerBase<NodeBase<ObjectNormal>, ObjectNormal> 
{ 
} 
+0

這將修復編譯錯誤,但它不能解決例如NodeNormals以外的子項可以存儲在ContainerNormals中的問題。如果我試圖讓一個孩子離開ContainerNormal,它將是NodeBase 而不是NodeNormal。因此,如果我想將任何方法添加到NodeNormal中,並將它們調用到我從ContainerNormal中取出的對象,那麼在沒有投射的情況下它將不起作用。 – 2011-03-09 19:42:55

+0

是的,在這種情況下,您需要從ContainerBase ,ObjectNormal>繼承的中間類,例如ContainerNormal :ContainerBase ,ObjectType>。我認爲它會變得混亂,突然你會有兩種類型的ContainerNormals來處理。您可以將屬性添加到ContainerNormal以直接獲取ObjectNormal,並添加一個方法來驗證它始終獲取ObjectNormals。有點混亂,但我認爲使用API​​明智會更容易。 – 2011-03-09 20:20:47

+0

如果您不打算實施多個將從NodeBase 繼承的類型,它應該是一個非問題。 – 2011-03-09 20:23:58

1

有一招你可以,我想會得到你想要的。您可以給NodeBase一個額外的必須從NodeBase繼承的通用參數。這給出了一個類似於遞歸的定義,但編譯器有一個辦法可以解決它。這樣的事情應該工作:

public class NodeBase<T, TNode> : 
    where T : ObjectBase 
    where TNode : NodeBase<T, TNode> 
{ 
    private ContainerBase<TNode, T> container; 

    protected NodeBase(ContainerBase<TNode, T> owner) 
    { container = owner; } 
} 

public class ContainerBase<NodeType, ObjectType> : 
    where NodeType : NodeBase<ObjectType, NodeType> 
    where ObjectType : ObjectBase 
{ 
    public NodeType GetItem() { ... } 
} 

public class NodeNormal : NodeBase<ObjectNormal, NodeNormal> 
{ 
    public NodeNormal(ContainerNormal owner) : 
     base(owner) { } 
} 

public class ContainerNormal : 
    ContainerBase<NodeNormal, ObjectNormal> 
{ 
    //GetItem would return NodeNormal here 
}