書くことによって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 は、このトピックに関するすばらしいブログ投稿を書きました。彼は、この他のcrtp から c# answer を介してリンクしています。
とはいえ、実装をこれに変更すると...
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
クラスの場合のサブクラスを作成します-いずれの方法でも、キャストを省略でき、その省略により、割り当てがすべての場合に有効であるため、無効なキャスト例外を回避できます。これを行うコストは、CRTP の価値の多くを犠牲にする以前に使用されていたように、すべての場所で自由に使用できなくなったことです。TreeNode<T>
Bar
Bar
TreeNode<Foo>
SetParent
T
私の同僚/友人は、「怒って使った」と正直に言うことができるまで、自分自身を言語/言語機能の初心者だと考えています。つまり、必要なことを達成する方法がないか、それを行うのが苦痛であることに不満を感じるほど十分に言語を知っています。ここには、ジェネリックはテンプレートではないという真実を反映した制限と違いがあるため、これはそのようなケースの 1 つになる可能性があります。