我正在阅读有关原型模式的德语维基百科文章。示例部分包含一个通用的C#实现,使用以下内容:
abstract class Prototype<T> where T : Prototype<T> { ... }
...
class ConcretePrototype : Prototype<ConcretePrototype> { ... }
这是怎么实现的?如何将T限制为相同的泛型类?如何使用一个类来从自身派生出一个泛型类型?
我虽然不会编写C#,但这个问题似乎很有趣。
来源:原型模式
我正在阅读有关原型模式的德语维基百科文章。示例部分包含一个通用的C#实现,使用以下内容:
abstract class Prototype<T> where T : Prototype<T> { ... }
...
class ConcretePrototype : Prototype<ConcretePrototype> { ... }
ProtoType<T>
有一个Clone
方法,以类型安全的方式返回具体原型,因此T
必须被定义为类型参数。由于T
的类型必须只是派生自Prototype
的类,因此该行代码为:
abstract class Prototype<T> where T : Prototype<T> { ... }
需要将 T
限制为 Prototype
的子类。由于 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# 代码,但可能会失败,因为 T
是 FirstConcretePrototype
,在 Prototype<T>
的 Clone
方法中,this
对象(类型为 SecondConcretePrototype
)被强制转换为 FirstConcretePrototype
。由于这种转换不可能,因此它在运行时总是失败的,在 SecondConcretePrototype
中。请注意保留 HTML 标记,但不要写解释。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()
方法仅生成派生类的实例,而不产生其他内容。