2013-04-22 71 views

回答

20

這與「Curiously Recurring Template Pattern」類似(但它不相同)。

它可以用來(除其他外)幫助將派生類中的方法的參數類型約束爲與派生類本身相同的類型。

Here's an interesting blog post from Eric Lippert on this subject

此主要用於強制從Entity<T>派生的類實現某種方法,該方法接受與派生類相同類型的參數。

在下面的代碼示例中,我們在Entity<T>類中聲明瞭一個方法DoSomethingWithTheSameTypeAsMe(),它接受T類型的參數。

由於通用約束,這將強制從Entity<T>派生的任何類實現DoSomethingWithTheSameTypeAsMe()版本,該版本採用派生類類型的參數。

這是有限的使用,它是非常令人困惑的閱讀,所以我同意Eric Lippert時,他說你應該避免這樣的代碼!

using System; 

namespace ConsoleApplication1 
{ 
    internal class Program 
    { 
     private static void Main() 
     { 
      var test1 = new Derived1(); 
      var test2 = new Derived2(); 

      test1.DoSomethingWithTheSameTypeAsMe(test1); 
      test2.DoSomethingWithTheSameTypeAsMe(test2); 
     } 
    } 

    public class Entity 
    { 
     public string Hello() 
     { 
      return "Hello, World."; 
     } 
    } 

    public abstract class Entity<T>: Entity where T: Entity<T> 
    { 
     public abstract void DoSomethingWithTheSameTypeAsMe(T item); 
    } 

    public sealed class Derived1: Entity<Derived1> 
    { 
     // You are forced to implement DoSomethingWithTheSameTypeAsMe() with a param type "Derived1". 
     // (i.e. the parameter is the same type as 'this') 

     public override void DoSomethingWithTheSameTypeAsMe(Derived1 item) 
     { 
      Console.WriteLine("Doing something with a Derived1 item: " + item.Hello()); 
     } 
    } 

    public sealed class Derived2: Entity<Derived2> 
    { 
     public override void DoSomethingWithTheSameTypeAsMe(Derived2 item) 
     { 
      Console.WriteLine("Doing something with a Derived2 item: " + item.Hello()); 
     } 
    } 
} 
+4

我很抱歉,但你不混淆C++模板與C#泛型? – 2013-04-22 08:38:37

+0

C#的原理不一樣嗎?重要的是這種類型受到這種限制。 – 2013-04-22 08:40:12

+4

我同意,這個答案是不正確的。在CRTP中,基類也是通用的,這裏不是這種情況。如果你在這裏說'Base ''與'Entity '相同,你也是不正確的,因爲在C#中你不能以這種方式訪問​​派生類的成員。 – 2013-04-22 08:40:16

2

它說,T必須或Entity<T>,這就是有,你限制T繼承。顯然T不能是Entity<T>因爲這是抽象的,所以它必須是從它繼承的東西。

4

它說,T必須Entity<T>類型或從該類型

派生雖然它似乎是自相矛盾它是有效的,有時是太有用,雖然情況是罕見的,往往可以在不同的處理更容易undertand方法。

它經常refered在C++行話爲Curiously recurring template pattern

在C#中的功能有些在C++中的具體類此圖案的使用模式時相比,更多的限制通常會這個樣子

class MyClass<ItemType> : Entity<MyClass<ItemType>> { 
    //... 
} 

或簡單地

時這可能是有用的
class MyClass : Entity<MyClass> { 
    //... 
} 

一個實例是具有在所述類型的屬性工作時。

假設您在運行時創建小部件列表。該列表包含從Entity<T>派生的所有類型,並根據來自屬性的元數據填充信息。在Entity<T>可以處理這個一勞永逸

void RegisterWidget(){ 
    var attributes = typeof(T).GetAttributes(); 
    //do what ever you need to 
} 

這當然與出約束的工作,但它仍然可能從功能角度看是有意義還是表現出打算,並可能在其他部分需要代碼

+0

爲什麼是DW?這個答案中沒有任何不正確的,它回答了這個問題 – 2013-04-22 08:39:20

12

雖然我評論過,我會堅持我的槳,因爲我也想說明基礎類型從中得到什麼。

只需:T必須繼承Entity<T>

這是一種自引用泛型,通常用於在基類中包含派生類類型(通過T)在方法和其他領域。它只是避免你必須投入東西或在派生類型中使用基礎引用。雖然我很少看到它用在我們的代碼中,但它可能非常有用。

我會注意到這是的不是表示基類可以突然訪問派生成員。它仍然只能看到由約束定義的最低已知類型(如果它們存在)。如果不存在約束條件,object是已知類型最低的。好處是從派生類型的角度以及它被賦予基類的代碼的清潔度。

在你的情況下,它會看到Entity<T>Entity成員。這是約束的原因。

標準用法是這樣的:

public class Customer : Entity<Customer> 
{ 
} 

public abstract class Entity<T> 
    where T : Entity<T> 
{ 
    public T Clone(T entityToClone) 
    { 
     return default(T); // Clone code here, returns derived type. 
    } 
} 


// Grants you... 
Customer clonedCustomer = currentCustomer.Clone(); 

// Instead of... 
Customer clonedCustomer = (Customer)currentCustomer.Clone(); 

// Ignore ethical constraints on cloning customers and definitely do not tell your sales team that you can ;-)