我正在阅读关于原型模式的德国维基百科文章。示例部分包含使用以下内容的通用C#实现:
abstract class Prototype<T> where T : Prototype<T> { ... }
...
class ConcretePrototype : Prototype<ConcretePrototype> { ... }
这是如何工作的?如何将T限制为相同的泛型类?如何使用类来使用自身从泛型类型派生?
我不是在编程C#,但这个似乎很有趣。
答案 0 :(得分:12)
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's excellent post on this topic。绝对值得一读,既可以帮助理解它,也可以理解它为什么会导致问题。
答案 1 :(得分:4)
好吧,基本上这只是将TypeParameter public function search($year)
{
$criteria=new CDbCriteria;
...
if($year != "") {
$criteria->compare('year',$year);
//$criteria->addSearchCondition('year',$year);
} else {
$criteria->compare('year',$this->year);
}
...
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
限制为继承自T
的类型,其类型为TypeParameter。
因此,只有从Prototype
继承的类才能作为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?
是有效的C#,但可能会失败,因为SecondConcretePrototype
是T
而FirstConcretePrototype
s Prototype<T>
- 方法, Clone
- 对象(类型this
)被转换为SecondConcretePrototype
。鉴于此转换是不可能的,它将始终在运行时失败,因为在FirstConcretePrototype
SecondConcretePrototype
转换为
public T Clone()
{
return (T)this.MemberwiseClone();
}
我知道这不是一个理智的男人会输入的东西,但是值得指出它并使这种模式有点不干净&#34; IMO,因为类型限制并不能保护你免受糟糕的事情的影响。
// still in SecondConcretePrototype ...
public FirstConcretePrototype Clone()
{
return (FirstConcretePrototype)this.MemberwiseClone(); // 'this' is of type SecondConcretePrototype
}
答案 2 :(得分:2)
我会尝试解释它,但首先让我们看一下简短但有效的例子:
<area shape="rect" coords="78,348,182,395" onclick="nextquestion()">
//输出
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
,它产生当前对象的副本。
如何将T限制为同一个泛型类?
没有理由不能将类型参数限制为从此基本抽象类继承的同一个类或派生类。这将导致类似:Clone()
或Prototype<Prototype<T>>
(两者都假定Derived继承自Prototype类)
如何使用类来使用自身从泛型类型派生?
当我们声明Prototype<Derived<T>>
类时,我们从ConcretePrototype1
(本身)派生它,因此我们让编译器知道Prototype<ConcretePrototype1>
模式应该使用Prototype<T>
作为其T参数。
这导致了方法ConcretePrototype1
返回Clone()
实例的逻辑,因为它就是我们的T。 ConcretePrototype1
类使用相同的逻辑。
简而言之,这是ConcretePrototype2
模式的抽象类签名:
Prototype
将其abstract class Prototype<T> where T : Prototype<T>
方法限制为仅生成派生类的实例,仅此而已。