私はC#のGenericsに関するMSDNの優れた記事を読んだ。
私の頭に浮かんだ問題は、なぜジェネリック制約を使用すべきかということでした。
たとえば、次のようなコードを使用するとします。
public class MyClass<T> where T : ISomething
{
}
私はすべての参照を切り替えることはできませんT
このクラスではISomething
?
このアプローチを使用する利点は何ですか?
あなたは尋ねる、 "私はすべての参照を切り替えることはできませんT
このクラスではISomething
? "と私はあなたが比較することを意味すると思う:
public class MyClass<T> where T : ISomething
{
public T MyProperty { get; set; }
}
を使用して:
public class MyClass
{
public ISomething MyProperty { get; set; }
}
第2の例では、MyProperty
のインスタンスであることが保証されていますISomething
。最初の例では、MyProperty
何でもT
たとえそれが特定のサブタイプであってもISomething
。具体的な実装を考えてみましょう。ISomething
:
public class MySomething : ISomething
{
public string MyOtherProperty { get; set; }
}
さて、最初の汎用の例を使用すると、次のようになります。
MyClass<MySomething> myClass = new MyClass<MySomething>();
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
あなたはアクセスすることができなくなりますSomeProperty
以来T
タイプのものであることだけが分かっているobject
。
タイプセーフティ。たとえば、コンテナを作成しているとします。そのコンテナに何かを渡して、後でコンテナをパラメータ化することによってキャストを行うことなく、適切な形式で取得できます。コンテナに格納したいと思うタイプの制約を定義するだけです。
違いの例は次のとおりです。List<>
画像リストは一般的ではありませんが、IListElement
どこでもgenericを代わりに使用していました。今、あなたはこのようなものを持っていると想像してください。
class Element : IListElement
{
public string Something { get; set; }
}
今私はちょうどできるlist.Add(element);
実際との違いはありませんList<Element>
。しかし、私がデータを収集するとき、それは別の話です。IListElement
私のデータを元に戻す必要があります。Something
それから。だから私はしなければならないだろう:
string s = ((Element)list[0]).Something;
ジェネリックでは私はちょうど行うことができます:
string s = list[0].Something;
多くのトラブルを救ってくれますが、それはそれよりも少し進んでいますが、私はあなたがこれからアイデアを得ることができると思います。
まずは、汎用クラスの汎用メソッド/メソッドのコード内でISomethingで定義されたメソッドを呼び出すことができます。 Tが任意の型であることが許されていれば、これは可能ではありません(あなたはいつでも実行時キャストを行うことができます)。
したがって、Tができることにコンパイル時の制約を課すことができ、コードを書くときにこれらの制約に頼って、実行時エラーをコンパイル時エラーに変えることができます。
はい、あなたはTの代わりに何かを使うことができますが、それは手動で行います閉じるジェネリック型を通常のクラスに変換します。それはもはやジェネリック型ではありません。 Tを使うことで、開いたあなたが望む数多くの種類のサブタイプに型安全性を損なうことなくコードを再利用することが、ここでの主な利点です。たとえば、ISomethingsのスタックを使用する場合、ISomethingをスタックにプッシュできますが、ISomethingの実際のサブタイプへのダウンキャストでポップが発生し、有用である必要があります。ダウンキャスティングは潜在的な障害点を作り出しますが、これは一般的なものではありませんStack<T>
ここでT:何か
クラスの消費者は、とりわけ型安全性の向上の恩恵を受ける。
class Widget : IPokable { }
// No generics
Widget w = (Widget)list[0]; // cast can fail
// With generics
Widget w = list[0];
ジェネリック医薬品がなければ、リストに含まれていればIPokable
オブジェクト、キャストはまだ必要です。
実装しているクラスは、ジェネリックオブジェクトの特定のメソッドを使用する利点があります。
class PokableList<T> where T : IPokable {
public T PokeAndGet() {
currentObj.Poke();
return currentObj;
}
}
このYouTube動画は、実際に一般的な制約の重要性を実証していますhttps://www.youtube.com/watch?v=GlqBRIgMgho。
今すぐ以下は長いテキストの答えになります。
「Genericは、ロジックをデータ型から切り離すのに役立ちます。私たちが あらゆる再利用可能なロジックを持つデータ型を添付してください。
しかし、いくつかのロジックは、特定のデータ型のみにアタッチすることができます。
public class CompareNumeric<UNNKOWDATATYPE>
{
public bool Compareme(UNNKOWDATATYPE v1, UNNKOWDATATYPE v2)
{
if (v1 > v2)
{return true;}
else
{return false;}
}
}
たとえば、上記のような単純な汎用クラスは、ある数値が他の数値より大きい場合に比較を行います。現在の数値は、数値データ型に比例します。この種の比較は、文字列のような非数値型では実行できません。
だから、 "int"型のクラスを使うと完全に有効です。
CompareNumeric<int> obj = new CompareNumeric<int>();
bool boolgreater = obj.Compare(10,20);
誰かが "double"データ型でそれを使っているなら、再び完全に有効です。
CompareNumeric<double> obj = new CompareNumeric<double>();
bool boolgreater = obj.Compare(100.23,20.45);
しかし、このロジックで文字列データ型を使用すると、望ましくない結果につながります。だから、ジェネリッククラスにどのタイプのタイプを付けることができるかを制約したり制限したりしたいのですが、これは「ジェネリック制約」を使って実現します。
CompareNumeric<string> obj = new CompareNumeric<string>();
bool boolgreater = obj.Compare(“interview”,”interviewer”);
ジェネリック型は、ジェネリッククラスの後に "WHERE"キーワードを使用してデータ型を指定することで、以下のコードに示すように制限することができます。クライアントが下のクラスで "string"データ型をアタッチしようとすると、許容されないため、望ましくない結果を避けます。
public class CompareNumeric<UNNKOWDATATYPE> where UNNKOWDATATYPE : int, double
{
}
int
またはdouble
ジェネリック型パラメータとして。あなたの最後の例で使用したものです。これを参照してくださいstackoverflow.com/questions/11770882/… - Bharat