20

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

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

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

4

4 回答 4

20

这类似于“奇怪重复的模板模式”(但不一样)。

它可用于(除其他外)帮助将派生类中方法的参数类型限制为与派生类本身相同的类型。

这是 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());
        }
    }
}
于 2013-04-22T08:35:35.817 回答
12

尽管我发表了评论,但我还是要坚持下去,因为我还想注意基本类型从中得到了什么。

很简单:T必须继承Entity<T>

它是一种经常使用的自引用泛型,使得基类可以在方法和其他区域中包含派生类类型(via )。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 ;-)
于 2013-04-22T08:52:37.530 回答
4

它说 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
}

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

于 2013-04-22T08:36:36.333 回答
2

它说 that Tmust be or inherit from Entity<T>,这就是T你要限制的东西。显然T不能是,Entity<T>因为那是抽象的,所以它必须是继承自它的东西。

于 2013-04-22T08:36:12.503 回答