2013-04-22 70 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 ;-)