什么是';类名<;T>;其中T:类名<;T>';做

本文关键字:lt gt 类名 什么 其中 | 更新日期: 2023-09-27 18:25:09

我正在阅读德语维基百科上关于原型模式的文章。示例部分包含一个使用以下内容的通用C#实现:

abstract class Prototype<T> where T : Prototype<T> { ... }
...
class ConcretePrototype : Prototype<ConcretePrototype> { ... }

这是怎么回事?如何将T限制在同一个泛型类中?如何使用类本身从泛型类型派生?

我不是在编程C#,但这个似乎很有趣。

来源:原型模式

什么是';类名<;T>;其中T:类名<;T>';做

ProtoType<T>有一个Clone方法,该方法以类型安全的方式返回具体原型,因此T必须定义为类型参数。由于T的类型必须仅是从Prototype派生的类,因此行:

abstract class Prototype<T> where T : Prototype<T> { ... }

需要将CCD_ 6约束为仅是CCD_。由于Prototype是通用的,因此必须在约束中指定Prototype<T>

理论上,ConcretePrototype的声明应该只是:

class ConcretePrototype : Prototype<> { ... }

(或类似的语法)。但是C#编译器不支持以这种方式推断类型参数。如果你放一些类似的东西:

class ConcretePrototype : Prototype<string> { ... }

您将得到一个编译错误,因为它知道它必须是Prototype<ConcretePrototype>,因为Prototype是受约束的。编译器需要对此进行明确声明,因此:

class ConcretePrototype : Prototype<ConcretePrototype> { ... }

我注意到Damien_The_Unbeliever击败了我,找到了推荐人,但无论如何,我还是要提到Eric Lippert在这个话题上的出色帖子。它绝对值得一读,既有助于理解它,也有助于了解它为什么会引起问题。

基本上,这只是将TypeParameter T限制为继承自Prototype的类型,并将其自身的类型作为TypeParameter。

因此,只有从Prototype<T>继承的类才能作为T传递。

(也许)工作示例:

class FirstConcretePrototype : Prototype<FirstConcretePrototype> { } // works
// propably not what the author wanted to happen but...
class SecondConcretePrototype : Prototype<FirstConcretePrototype> { } // works (at least compiles) too, funny huh?

请注意,SecondConcretePrototype是有效的C#,但可能会失败,因为TFirstConcretePrototype,而在Prototype<T>Clone-方法中,this-对象(类型为SecondConcretePrototype)会被广播到FirstConcretePrototype。如果这种强制转换是不可能的,那么它在运行时总是会失败,因为在SecondConcretePrototype

public T Clone()
{
    return (T)this.MemberwiseClone();
}

转换为

// still in SecondConcretePrototype ...
public FirstConcretePrototype Clone()
{
    return (FirstConcretePrototype)this.MemberwiseClone(); // 'this' is of type SecondConcretePrototype 
}

我知道这不是一个理智的人会键入的,但值得指出的是,这让这种模式有点"不干净",因为类型限制并不能保护你不做糟糕的事情。。

失败的例子

class AnyType{ }
class ThirdConcretePrototype : Prototype<AnyType> { } // fails at compiletime, AnyType does not inhertit from Prototype<T>

我将尝试解释它,但首先让我们看看简短但有效的示例:

abstract class Prototype<T> where T : Prototype<T> 
{
    public T Clone()
    {
        return this.MemberwiseClone() as T;
    }
}
class ConcretePrototype1 : Prototype<ConcretePrototype1> 
{
    public int Id { get; set; }
    public string Name { get; set; }
}
class ConcretePrototype2 : Prototype<ConcretePrototype2> 
{
    public int Id { get; set; }
    public string Name { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        ConcretePrototype1 inst1 = new ConcretePrototype1()
        {
            Id = 1,
            Name = "Jon Skeet"
        };
        ConcretePrototype2 inst2 = new ConcretePrototype2()
        {
            Id = 2,
            Name = "Frodo Torbins"
        };
        ConcretePrototype1 copy1 = inst1.Clone();
        ConcretePrototype2 copy2 = inst2.Clone();
        Console.WriteLine(copy1.Name + "  " + copy1.GetType().Name);
        Console.WriteLine(copy2.Name + "  " + copy2.GetType().Name);
    }
} 

//输出

Jon Skeet   ConcretePrototype1
Frodo Torbins ConcretePrototype2

说明:

这是怎么回事?

正如您所看到的,原型模式只有一个方法Clone(),它生成当前对象的副本。

如何将T限制在同一个泛型类中?

并没有理由不能将类型参数限制为从这个抽象基类继承的同一类或派生类。这将导致类似于:Prototype<Prototype<T>>Prototype<Derived<T>>(这两种方法都假设Derived继承自Prototype类)的

如何使用类本身从泛型类型派生?

当我们声明ConcretePrototype1类时,我们从Prototype<ConcretePrototype1>(本身)派生它,所以我们让编译器知道Prototype<T>模式应该使用ConcretePrototype1作为它的T参数。这导致了方法Clone()返回ConcretePrototype1实例的逻辑,因为它就是我们的T。相同的逻辑用于ConcretePrototype2类。

简而言之,这个Prototype模式的抽象类签名:

abstract class Prototype<T> where T : Prototype<T> 

将其Clone()方法限制为仅生成派生类的实例,而不生成其他实例。