2014-04-21 90 views

回答

4

這是recurring template pattern,通常用於基類可以靜態引用其真實類型。這是在試圖保留類型安全,這樣的參數或在基類返回簡稱值跟蹤當前的類型層次結構中的如

public class Animal<T> where T : Animal<T> 
{ 
    public abstract T GiveBirth(); 
} 

public class Cat : Animal<Cat> 
{ 
    public override Cat GiveBirth() { return new Cat(); } 
} 

不帶類型參數的Animal基類的方法只會做能夠將GiveBirth的返回類型定義爲Animal,這可能會降低客戶端的類型安全性。

如果您控制整個層次結構並確保類提供正確的類型參數,但它可能是可接受的,但請注意它可能被濫用,例如,

public class Cat : Animal<Dog> { ... } 

另一個缺點是任何客戶端需要考慮到一般類型參數的,如果他們想要施加到基類例如

public static void Feed<T>(Animal<T> animal) where T : Animal<T> { ... } 
public static void Feed<T>(T animal) where T : Animal<T> { ... } 
+0

感謝了很多,但在什麼樣的問題,我們應該使用這種模式? – RezaRahmati

+0

@RezaRahmati - 我已經添加了一個例子,如果有幫助? – Lee

0

它看起來是保證類型是二維的(如果這個詞在這裏是有道理的)。

例如:Node<int>最終將成爲Node<Node<int>>

+1

我覺得編譯器會顯示錯誤,因爲T IN節點爲int,但在節點>爲節點 RezaRahmati

2

這是一個奇怪的循環模式的例子。 Eric Lippert對此有an excellent article,包括爲什麼你通常應該避免它。

它可能被擴展這樣的:

public class MyChild : MyClass<MyChild> 

模式並沒有真正的線索,你爲爲什麼你想要這個通用的。這與大多數泛型/約束不同......例如。如果我有List<Giraffe>我可以看到關係;如果我有MyGeneric<T, U> where T : IComparer<U>,我可以看到T會做什麼。用T : MyClass<T>,我真的沒有關於這裏的關係或用法的提示。也許有一個......

abstract T Instance { get; } 

...你想擁有的MyChild更強打字在MyChild的情況。

作爲一個例子,爲什麼這不是很好,你可以有MyOtherClass : MyClass<MyChild>,或者你可以有MyGrandchild : MyChild,這兩者都可能是你試圖強制執行的。

1

對於類型,這將僅由一個抽象基類具有繼承的單層,使用所描述的圖案將使得有可能爲抽象基類型包括,當被叫的任何構件上的方法派生類型,將返回該派生類型的成員。這可能是一個有用的設計功能,允許使用比其他方式更清晰的主叫方代碼。這種設計最大的問題是,因爲.NET不支持協變泛型類參數,所以這種方法不適用於多層繼承。

鑑於abstract class AnimalBase<T> where T:AnimalBase<T>,與法T Clone()class Cat: AnimalBase<Cat>,代碼可以說var newCat = someCat.Clone(); newCat.Meow();而不是說var newCat = (Cat)(someCat.Clone()); newCat.Meow();。不幸的是,沒有辦法有SiameseCat正確地從Cat派生出來,因爲唯一的方法是有mySiameseCat.Clone();返回一個SiameseCat將有SiameseCat派生自AnimalBase<SiameseCat>,但這將阻止它從Cat派生。

如果不是有一個類限制到它自己的類型,而是定義一個通用接口並限制它,可以避免這樣的困難。在執行IAnimal<SiameseCat>時,SiameseCatCat派生將沒有問題。此外,接口是協變的,因此實現IAnimal<SiameseCat>的類型也可以隱含地實現IAnimal<Cat> [如果Cat是一個抽象類型,它沒有實現接口本身]。每個派生類都必須提供它自己的實現,其返回值隨通用類型參數而變化,但從調用者的角度來看,接口類型可以與派生類完美行爲。

相關問題