C#でこの見かけの自己参照の目的は何ですか?


21

私のプロジェクトの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>ですか?どのような特定の目的に役立ちますか?



7
賛成票と賛成票にもかかわらず、コミュニティはこれについて間違っていると思います。これは、このサイトが何であるかの本質である、具体的かつ自明ではない設計決定に関係する明確に述べられた質問です。
ロバートハーベイ

それが閉じられたら、ただぶらぶらして、それを再び開きます。
デビッドアルノ

F-bounded polymorphismの概念を読んでください:)
Eyvind

1
@Eyvindは実際にやった。F-Boundedポリモーフィズムについて読むことに興味がある人のために、リンクstaff.ustc.edu.cn/~xyfeng/teaching/FOPL/lectureNotes/…にあります
Xami Yen

回答:


13

クラスが同じ型のベースから継承している理由を理解するのに役立つことがありますか?

ではなく、から継承してPage<T>Tますが、それ自体はから派生した型によってパラメーター化されるように制限されていますBasePage<T>

理由を推測するにTは、typeパラメーターが実際にどのように使用されているかを調べる必要があります。掘り下げた後、継承チェーンを上に移動すると、このクラスが表示されます。

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渡すページのリポジトリです(こちらを参照)。typeIdcontentService.Create


5

これの一般的な使用法の1つは、自己型の概念、つまり現在の型に解決される型パラメーターに関連しています。clone()メソッドでインターフェースを定義したいとしましょう。clone()この方法は、常にそれが呼び出されたクラスのインスタンスを返す必要があります。そのメソッドをどのように宣言しますか?自己型を持つジェネリックシステムでは、簡単です。あなたはそれが戻ると言うだけselfです。したがって、クラスがある場合Foo、cloneメソッドを宣言して返す必要がありますFoo。Javaおよび(大まかな検索から)C#では、これはオプションではありません。代わりに、このクラスで見られるような宣言が表示されます。これは自己タイプと同じものではなく、それが提供する制限がより弱いことを理解することが重要です。両方から派生したFooBarクラスがある場合BasePage、(私が間違っていなければ)によってパラメーター化されるFooを定義できますBar。それは役に立つかもしれませんが、通常、ほとんどの場合、これは自己タイプのように使用され、他のタイプに置き換わることができても、それはあなたがすべきことではないことを理解しています。私はずっと前にこの考えをいじりましたが、Javaジェネリックの制限のために努力する価値はないという結論に達しました。もちろん、C#ジェネリックはより完全に機能しますが、この同じ制限があるようです。

このアプローチが使用される別の機会は、ツリーや他の再帰構造などのグラフのようなタイプを構築するときです。この宣言により、型はPageの要件を満たすことができますが、型はさらに洗練されます。これはツリー構造で表示される場合があります。たとえば、aをNodeパラメーター化しNodeて、実装が、ノードのタイプを含むツリーだけでなく、ノードの特定のサブタイプ(通常は独自のタイプ)であることを定義できるようにする場合があります。


3

実際にコードを書いた人であることから、Filipが正しく、自己参照ジェネリックが基本クラスで型指定されたCreateメソッドを提供するのに便利であることを確認できます。

彼が言及したように、まだ多くのリフレクションが進行中であり、最終的にはタイプの名前のみがページタイプの解決に使用されます。これは、動的モデルもロードできるためです。つまり、最初に作成したCLR型にアクセスせずにモデルを具体化できるためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.