28


9

C#で汎用制約を使用する理由

MSDNでC#のGenericsに関する素晴らしい記事を読みました。

私の頭に浮かんだ質問は-なぜ一般的な制約を使用する必要があるのですか?

たとえば、次のようなコードを使用する場合:

public class MyClass where T : ISomething
{
}

このクラスの「T」のすべての参照を「ISomething」に切り替えることはできませんか?

このアプローチを使用する利点は何ですか?

7 Answer


47


「このクラスの「T」のすべての参照を「ISomething」に切り替えることはできませんか?」だから私はあなたが比較するつもりだと思う:

public class MyClass where T : ISomething
{
    public T MyProperty { get; set; }
}

と:

public class MyClass
{
    public ISomething MyProperty { get; set; }
}

2番目の例では、「MyProperty」は「ISomething」のインスタンスであることが保証されているだけです。 最初の例では、 MyProperty`は、それが ISomething`の特定のサブタイプであっても、 `T`が何であれです。 「ISomething」の具体的な実装を検討してください。

public class MySomething : ISomething
{
    public string MyOtherProperty { get; set; }
}

さて、最初の一般的な例を使用すると、次のようになります。

MyClass myClass = new MyClass();
Console.WriteLine(myClass.MyProperty.MyOtherProperty);

一方、2番目の例を使用した場合、「MyOtherProperty」は「ISomething」であることがわかっているだけなので、アクセスできません。

MyClass myClass = new MyClass();
Console.WriteLine(myClass.MyProperty.MyOtherProperty); // Won't compile, no property "MyOtherProperty"

別の注意として、これらの型制約が有用な理由は、 MyProperty(型` T`)を参照し、 `ISomething`のメンバーにアクセスできるからです。 つまり、「ISomething」が次のように宣言された場合:

public interface ISomething
{
    public string SomeProperty { get; set; }
}

次に、 `MyProperty.SomeProperty`にアクセスできます。 「where T:ISomething」を省略した場合、「T」は「object」型のみであるため、「SomeProperty」にアクセスできません。


8


安全性を入力します。 たとえば、コンテナを作成しているとします。 そのコンテナに何かを渡し、コンテナをパラメータ化することで、後でキャストを行うことなく適切な形式で取得できます。 コンテナに保存したいものの種類に対する制約を定義するだけです。


4


これは、単に `List <>`を使用した場合の違いの例です

画像リストは汎用ではありませんが、代わりに汎用を使用するすべての場所で「IListElement」を使用します。 今、このようなオブジェクトがあると想像してください。

class Element : IListElement
{
   public string Something { get; set; }
}

これで、 `list.Add(element);`を実行できますが、実際の `List`と違いはありません。 しかし、データを取得するときは別の話です。「IListElement」を使用するリストを使用する場合、「Something」を取り出すためにデータをキャストし直す必要があります。 したがって、私はする必要があります:

string s = ((Element)list[0]).Something;

ジェネリックを使用すると、次のことができます。

string s = list[0].Something;

多くの手間を省くことができます。もちろんそれはそれよりも少し先になりますが、これからアイデアを得ることができると思います。


2


まず、ジェネリッククラスのジェネリックメソッドのコード内でISomethingで定義されたメソッドを呼び出すことができます。 Tが任意の型であることが許可されている場合、これは不可能です(ただし、常にランタイムキャストを実行できます)。

そのため、Tの可能性にコンパイル時の制約を適用できるため、コードを記述する際にこれらの制約に依存することができ、ランタイムエラーをコンパイル時エラーに変換できます。


1


はい、Tの代わりにISomethingを使用できますが、それは通常のクラスへのジェネリック型を手動で* close します。 もうジェネリック型ではありません。 Tを使用すると、タイプ open を必要な数のISomethingサブタイプに維持できます。 *ここでは、型の安全性を損なうことなくコードを再利用することが重要な利点です。たとえば、ISomethingsのスタックを使用する場合、ISomethingをスタックにプッシュできますが、ISomethingの実際のサブタイプにダウンキャストしてポップを発生させる必要があります役に立つ。 ダウンキャスティングは潜在的な障害ポイントを作成しますが、T:ISomethingのような一般的な `Stack`にはありません。


0


クラスの消費者は、とりわけ型安全性の向上という利点を享受します。

class Widget : IPokable { }

// No generics
Widget w = (Widget)list[0]; // cast can fail

// With generics
Widget w = list[0];

ジェネリックがない場合、リストに「IPokable」オブジェクトが含まれていた場合、キャストが必要です

実装しているクラスは、ジェネリックオブジェクトで特定のメソッドを使用する利点があります。

class PokableList where T : IPokable {
    public T PokeAndGet() {
        currentObj.Poke();
        return currentObj;
    }
}


0


このyoutubeビデオは、一般的な制約https://www.youtube.com/watch?v=GlqBRIgMghoの重要性を実際に示しています。

次に、長いテキストによる回答を示します。

_ 「Genericは、ロジックをデータ型から分離するのに役立ちます。 そのため、再利用性を高めるために、任意のデータ型に任意のロジックを付加します。」 _

しかし、多くの場合、一部のロジックは特定のデータ型のみに付加できます。

public class CompareNumeric
{
        public bool Compareme(UNNKOWDATATYPE v1, UNNKOWDATATYPE v2)
        {
            if (v1 > v2)
            {return true;}
            else
           {return false;}
        }
}

上記の例は、1つの数値が他の数値よりも大きい場合に比較を行う単純な汎用クラスです。 現在、大小比較は数値データ型に固有のものです。 この種の比較は、文字列のような非数値型では実行できません。

そのため、一部のクラスが「int」型のクラスを使用する場合、完全に有効です。

CompareNumeric obj = new CompareNumeric();
bool boolgreater = obj.Compare(10,20);

誰かが再び「double」データ型でそれを使用すると、完全に有効になります。

CompareNumeric obj = new CompareNumeric();
bool boolgreater = obj.Compare(100.23,20.45);

ただし、このロジックで文字列データ型を使用すると、望ましくない結果が生じます。 そのため、「ジェネリック制約」を使用することで実現される、ジェネリッククラスにどの種類のタイプをアタッチできるかを制限または制約したいと思います。

CompareNumeric obj = new CompareNumeric();
bool boolgreater = obj.Compare(“interview”,”interviewer”);

次のコードに示すように、ジェネリッククラスの後に「WHERE」キーワードを使用してデータタイプを指定することにより、ジェネリックタイプを制限できます。 これで、クライアントが「string」データ型を以下のクラスにアタッチしようとすると、許可されないため、望ましくない結果が回避されます。

public class CompareNumeric where UNNKOWDATATYPE : int, double
{

}