19

自己参照(「再帰的」)タイプのパラメーター制約を追加することにより、単純なインターフェースをより複雑にすることがよくあります。たとえば、私はこれを変えるかもしれません:

interface ICloneable
{
    ICloneable Clone();
}

class Sheep : ICloneable
{
    ICloneable Clone() { … }
} //^^^^^^^^^^

Sheep dolly = new Sheep().Clone() as Sheep;
                                //^^^^^^^^

の中へ:

interface ICloneable<TImpl> where TImpl : ICloneable<TImpl>
{
    TImpl Clone();
}

class Sheep : ICloneable<Sheep>
{
    Sheep Clone() { … }
} //^^^^^

Sheep dolly = new Sheep().Clone();

主な利点:実装型(などSheep)は、基本型の代わりにそれ自体を参照できるようになり、型キャストの必要性が減りました(コードの最後の行で示されています)。

これは非常に便利ですが、これらの型パラメーターの制約は直感的ではなく、より複雑なシナリオでは理解するのが非常に困難になる傾向があることにも気づきました。*)

質問:同じ効果または同様の効果を実現するが、把握しやすい別のC#コードパターンを知っている人はいますか?


*)このコードパターンは、直感的でなく、理解しにくい場合があります。たとえば、次のようになります。

  • 宣言X<T> where T : X<T>は再帰的であるように見え、なぜコンパイラが無限ループに陥らないのか不思議に思うかもしれません。 「もしTが、ならX<T>X<T>は本当にX<X<…&lt;T>…&gt;>。」と推論します。(しかし、制約は明らかにそのように解決されません。)

  • 実装者にとっては、の代わりにどのタイプを指定する必要があるかが明確でない場合がありますTImpl。(制約は最終的にそれを処理します。)

  • さまざまなジェネリックインターフェイス間のタイプパラメータとサブタイピング関係をミックスに追加すると、物事はかなりすぐに管理できなくなります。

4

2 に答える 2

19

主な利点:実装型は、基本型ではなくそれ自体を参照できるようになり、型キャストの必要性が減りました。

型制約がそれ自体を参照しているように見えるかもしれませんが、実装型に同じことを強制しますが、実際にはそうではありません。人々はこのパターンを使用して、「このメソッドのオーバーライドはオーバーライドするクラスの型を返さなければならない」という形式のパターンを表現しようとしますが、これは実際には型システムによって表現または強制される制約ではありません。ここに例を示します。

https://ericlippert.com/2011/02/02/curiouser-and-curiouser/

これは非常に便利ですが、これらの型パラメーターの制約は直感的ではなく、より複雑なシナリオでは理解するのが非常に困難になる傾向があることにも気づきました。

うん。私はこのパターンを避けようとします。推論するのは難しいです。

同じ効果または同様の効果を実現するが、把握しやすい別のC#コードパターンを知っている人はいますか?

C#ではありません。この種のことに興味がある場合は、Haskell型システムを検討することを検討してください。Haskellの「上位型」は、これらの種類の型パターンを表すことができます。

宣言X<T> where T : X<T>は再帰的であるように見え、なぜコンパイラが無限ループに陥らないのか不思議に思うかもしれません。「もしTが、ならX<T>X<T>は本当にX<X<…&lt;T>…&gt;>。」と推論します。

このような単純な関係について推論するときに、コンパイラーが無限ループに陥ることはありません。ただし、共変性を伴うジェネリック型の名目上のサブタイピングは、一般に決定できません。コンパイラーを無限後退させる方法はいくつかありますが、C#コンパイラーはこれらを検出せず、無限後退に着手する前にそれらを防止します。(ただし、Roslynコンパイラでこれの検出を追加したいと思っていますが、これから説明します。)

これに興味がある場合は、このテーマに関する私の記事を参照してください。リンク先の論文も読みたくなるでしょう。

https://ericlippert.com/2008/05/07/covariance-and-contravariance-part-11-to-infinity-but-not-beyond/

于 2012-01-15T01:42:26.370 に答える
7

残念ながら、これを完全に防ぐ方法はなく、ICloneable<T>型の制約のないジェネリックで十分です。制約は、可能なパラメーターをそれ自体が実装するクラスにのみ制限します。これは、それらが現在実装されているパラメーターであることを意味するものではありません。

言い換えれば、Cow実装ICloneable<Cow>する場合でも、簡単にSheep実装を作成できますICloneable<Cow>

ICloneable<T>私は2つの理由で制約なしで単純に使用します:

  1. 間違った型パラメータを使用することを間違えることはないだろうと私は真剣に疑っています。

  2. インターフェイスは、コードの他の部分のコントラクトであり、自動操縦でのコーディングには使用されません。コードの一部が期待ICloneable<Cow>し、それを実行できるaを渡したSheep場合、その時点からは完全に有効であるように見えます。

于 2012-01-15T02:34:13.220 に答える