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

本文关键字:是什么 意思 约束 类型 参数 | 更新日期: 2023-09-27 18:10:13

我正在看一些代码,我不明白在下面的类定义中一个特定的约束是什么意思:

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

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

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

这类似于"奇怪的循环模板模式"(但并不相同)。

它可以用来帮助约束派生类中方法的参数类型,使其与派生类本身的类型相同。

这是Eric Lippert关于这个主题的一篇有趣的博客文章。

它的主要用途是强制从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());
        }
    }
}

尽管我已经注释过了,但我还是要把我的桨插进去,因为我还想注意基本类型从中得到什么

简单: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 ;-)

它说T必须是类型Entity<T>或从该类型派生

虽然看起来有点矛盾,但它是有效的,有时也是有用的,尽管这种情况很少,而且通常可以用不同的更容易理解的方式处理。

在c++术语中,它通常被称为奇怪的反复出现的模板模式

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

这当然可以在没有约束的情况下工作,但从功能的角度来看,它仍然是有意义的,或者显示意图,并且可能需要在代码的其他部分

它说T必须是或继承自Entity<T>,这就是您所限制的T。显然,T不能是Entity<T>,因为那是抽象的,所以它必须是从它继承的东西。