私のプロジェクトの1つで使用するためにPiranha( http://piranhacms.org/ )と呼ばれるオープンソースCMSを評価しています。次のコードは、少なくとも私にとっては興味深いものであり、少しわかりにくいものでした。クラスが同じ型のベースから継承する理由を理解するのに役立つ人はいますか?
public abstract class BasePage<T> : Page<T> where T : BasePage<T>
{
/// <summary>
/// Gets/sets the page heading.
/// </summary>
[Region(SortOrder = 0)]
public Regions.PageHeading Heading { get; set; }
}
BasePage<T>
のクラスが定義されている場合、なぜPage<T> where T: BasePage<T>
から継承するのですか?具体的な目的は何ですか?
クラスが同じ型のベースから継承する理由を理解するのに役立つ人はいますか?
そうではなく、Page<T>
から継承していますが、T
自体は、BasePage<T>
から派生した型によってパラメーター化されるように制限されています。
理由を推測するには、型パラメーターT
が実際にどのように使用されているかを調べる必要があります。少し掘り下げた後、継承チェーンを上に移動すると、次のクラスにたどり着きます。
( github )
public class GenericPage<T> : PageBase where T : GenericPage<T>
{
public bool IsStartPage {
get { return !ParentId.HasValue && SortOrder == 0; }
}
public GenericPage() : base() { }
public static T Create(IApi api, string typeId = null)
{
return api.Pages.Create<T>(typeId);
}
}
私が知る限り、ジェネリック制約の唯一の目的は、Create
メソッドが可能な限り最小の抽象型を返すことを確認することです。
しかし、それが価値があるかどうかはわかりませんが、おそらくその背後にいくつかの理由があるか、それが便宜上の理由であるか、多分その背後に実質がないため、キャストを回避するための過度に手の込んだ方法です(ところで、私はここでそうであることを意味しているわけではありません、私は人々がそれを時々行うと言っています)。
これはリフレクションを回避できないことに注意してください。api.Pages
はtypeof(T).Name
を取得するページのリポジトリであり、typeId
としてcontentService.Create
メソッドに渡されます( ここ )。
これの一般的な用途の1つは、自己型の概念に関連しています。現在の型に解決される型パラメーターです。 clone()
メソッドを使用してインターフェースを定義するとします。 clone()
メソッドは、呼び出されたクラスのインスタンスを常に返す必要があります。そのメソッドをどのように宣言しますか?自己型を持つジェネリックシステムでは、それは簡単です。 self
を返すと言うだけです。したがって、クラスFoo
がある場合、Foo
を返すようにcloneメソッドを宣言する必要があります。 Javaおよび(大ざっぱな検索から)C#では、これはオプションではありません。代わりに、このクラスで見られるような宣言が表示されます。これは同じことではないことを理解することが重要です。自己型として、それが提供する制限はより弱いです。Foo
とBar
クラスがあり、どちらもBasePage
から派生している場合、(私がそうでない場合)誤って)FooをBar
でパラメーター化するように定義します。これは便利かもしれませんが、通常、ほとんどの場合、これは自己型のように使用されます。他のタイプに置き換えると、それはあなたがすべきことではありません。私はずっと前にこのアイデアを試しましたが、Javaジェネリックの制限があるため、努力する価値がないと結論しました。C#ジェネリックはもちろんより完全に機能していますが、これと同じ制限があるようです。
このアプローチが使用される別の時期は、ツリーや他の再帰的構造などのグラフのようなタイプを構築するときです。この宣言により、型はPageの要件を満たすことができますが、型をさらに洗練することができます。これはツリー構造で表示される場合があります。たとえば、Node
はNode
によってパラメーター化され、実装が、Nodeのタイプを含むツリーだけでなく、特定のサブ-type of Node(通常は独自のタイプ。)ここで何が起こっているかがもっと多いと思います。
実際にコードを書いた人であることで、Filipが正しいことと、自己参照ジェネリックが、基本クラスに型指定されたCreateメソッドを提供するのに実際に便利であることを確認できます。
彼が述べたように、まだ多くのリフレクションが行われており、最終的にはタイプの名前のみがページタイプの解決に使用されます。これは、動的モデルもロードできるためです。つまり、最初にモデルを作成したCLRタイプにアクセスしなくてもモデルを実体化できます。