考虑一下如果我们通过编写偏离 CRTP 约定会发生什么......
public class Foo : TreeNode<Foo>
{
}
public class Bar : TreeNode<Foo> // parting from convention
{
}
...然后调用上面的代码如下:
var foo = new Foo();
var foobar = new Bar();
foobar.AddChild(foo);
AddChild
电话抛出一个InvalidCastException
说法Unable to cast object of type 'Bar' to type 'Foo'.
关于 CRTP 习惯用法 - 仅约定要求泛型类型与声明类型相同。该语言必须支持不遵循 CRTP 约定的其他情况。Eric Lippert 写了一篇关于这个主题的精彩博客文章,他通过 c# answer 从另一个 crtp链接。
综上所述,如果您将实现更改为此...
public class TreeNode<T> where T : TreeNode<T>
{
public void AddChild(T a_node)
{
a_node.SetParent(this);
}
void SetParent(TreeNode<T> a_parent)
{
m_parent = a_parent;
}
TreeNode<T> m_parent;
}
...之前抛出的上述代码InvalidCastException
现在可以工作。更改使m_Parent
类型为TreeNode<T>
; 使this
类型T
在Foo
类的情况下或TreeNode<T>
在Bar
类情况下的子类Bar
继承自TreeNode<Foo>
-任何一种方式都允许我们省略强制转换SetParent
并通过这种省略避免无效的强制转换异常,因为赋值在所有情况下都是合法的。这样做的代价是不再能够T
像以前那样在所有地方自由使用,这牺牲了 CRTP 的大部分价值。
我的一位同事/朋友认为自己是语言/语言功能的新手,直到他可以诚实地说他“在愤怒中使用它”;也就是说,他对语言的了解足以让他感到沮丧,因为要么无法完成他需要的事情,要么这样做很痛苦。这很可能是其中一种情况,因为这里存在一些限制和差异,这与泛型不是模板这一事实相呼应。