这个参数类型约束是什么意思?

时间:2013-04-22 08:32:14

标签: c#

我正在查看一些代码,但我不明白特定约束在以下类定义中的含义:

internal abstract class Entity<T> : Entity
    where T : Entity<T>
{ ... }

我不明白这对参数类型T意味着什么。

4 个答案:

答案 0 :(得分: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());
        }
    }
}

答案 1 :(得分: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 ;-)

答案 2 :(得分:4)

它表示T必须是Entity<T>类型或源自该类型

虽然看似矛盾但它有效并且有时候也很有用,尽管这种情况很少见,并且通常可以用不同的方式处理。

在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
}

这当然可以解决约束问题,但从功能角度来看仍然有意义,或者显示意图,并且可能在代码的其他部分需要它

答案 3 :(得分:2)

它表示T必须是Entity<T>或继承自T,而T是您所限制的Entity<T>。显然{{1}}不能是{{1}},因为它是抽象的,所以它必须是从它继承的东西。