1. ジェネリックの概要
ジェネリック クラスとジェネリック メソッドは、対応する非ジェネリック クラスとメソッドでは実現できない再利用性、型安全性、および高効率を組み合わせています。ジェネリックは、コンテナー (コレクション) およびコンテナー上で動作するメソッドで広く使用されています。 .NET Framework 2.0 クラス ライブラリは、新しい名前空間 System.Collections.Generic を提供します。これには、いくつかの新しいジェネリック ベースのコンテナー クラスが含まれています。
ジェネリックスの変数型パラメーター: 通常は T が使用されますが、キーワード以外の任意の予約語も使用できます。
すべての変数型 T は、シンボルの形式でプレースホルダーを使用します。すべてのドット記号は、実行時に渡される実際の型に置き換えられます。
2. ジェネリックの利点
共通言語ランタイムの初期バージョンと C# 言語の制限については、ジェネリックが解決策を提供します。以前の型の一般化は、型をグローバル基本クラス System.Object に変換することによって実現されました。 .NET Framework 基本クラス ライブラリの ArrayList コンテナ クラスは、この制限の一例です。 ArrayList は、使用中に変更することなく任意の参照型や値の型を格納できる非常に便利なコンテナ クラスです。
ArrayList list = new ArrayList(); list.Add(1); list.Add(175.50); list.Add("hello kitty");
double sum = 0; foreach(int value in list){ sum += value; }
|
短所:
利便性には代償が伴い、ArrayList に追加される参照型または値型を柔軟に非表示にする必要があります。 System.Object にアップキャストします。要素が値型の場合、リストに追加するときにボックス化し、取得するときにボックス化を解除する必要があります。型変換とボックス化およびボックス化解除操作はすべてパフォーマンスを低下させます。大規模なコンテナーを反復する必要がある場合、ボックス化とボックス化解除の影響は大きくなる可能性があります。もう 1 つの制限は、ArrayList が任意の型を Object に変換するときに、コンパイル時に顧客コード内の sum+=vlaue のようなエラーを防ぐことができないことです。 List
コンテナの場合、次のようにコンテナに要素を追加するのと同じ操作です。 listInt.Add (200); )
合計 += 値;もう少し複雑なコードを使用すると、作成するテーブルが ArrayList よりも安全であるだけでなく、特にテーブル内の要素が値型である場合に大幅に高速になるというメリットがあります。 3. ジェネリック型パラメーター
ジェネリック型またはジェネリック メソッドの定義では、型パラメーターはプレースホルダーであり、通常は大文字です (キーワード以外の予約語名も使用できます)。 T.クライアント コードがこの型の変数を宣言してインスタンス化する場合、T をクライアント コードで指定されたデータ型に置き換えます。 「ジェネリックス」で示されている List クラスなどのジェネリック クラスは、真の型ではなく、型の設計図に近いため、そのままでは使用できません。 MyList を使用するには、クライアント コードで山括弧内に型パラメータを指定して構築型を宣言し、インスタンス化する必要があります。この特定のクラスの型パラメータは、コンパイラによって認識される任意の型にすることができます。次のように、異なる型パラメーターを使用して、構築された型のインスタンスをいくつでも作成できます。
ListlistInt = new List(); List List(); ListlistString = new List();
4. ジェネリック型パラメーターの制約
ジェネリックには、次の 5 つの制約があります。 パラメータの型は値型である必要があります
where T : class | パラメータの型は参照型でなければなりません |
where T : new() | パラメータの型には引数のないパブリックなコンストラクタが必要です。他の制約と組み合わせて使用する場合は、new() 制約を最後に配置する必要があります。 |
ここで、T : <基本クラス名> | パラメータの型は、指定された基本タイプ、または指定された基本タイプから派生したサブクラスである必要があります |
ここで、T : <インターフェース名> | パラメータのタイプ指定されたインターフェイス、または指定されたインターフェイスの実装である必要があります。制約は複数のインターフェースに指定できます。インターフェイス制約は汎用的なものにすることもできます。 |
| 無制限の型パラメータ: |
| 特定の型パラメータがこれらの演算子をサポートする保証がないため、!= と == を使用して可変型のインスタンスを比較することはできません。 System.Object との間で変換したり、任意のインターフェイス型に明示的に変換したりできます。 |
は null と比較できます。修飾されていない型パラメーターが null と比較される場合、型パラメーターが値型の場合、比較の結果は常に false になります。
型制約なし: 制約がジェネリック型パラメーターの場合、それは裸の型制約と呼ばれます。
-
class List{ void Add(List items) where U :上記の例では、Add メソッドのコンテキストの T は型なし制約ですが、List クラスのコンテキストの T は無制限の型パラメーターです。
型なし制約は、ジェネリック クラスの定義でも使用できます。型なし制約も他の型パラメータと一緒に山括弧で宣言する必要があることに注意してください:
//naked typeconstraint
public class MyClass where T : V
コンパイラは、型なし制約が System.Object から継承されるものとしてのみ考慮するため、型なし制約を持つジェネリック クラスの使用は非常に制限されています。 2 つの型パラメーター間の継承関係を強制する場合は、ジェネリック クラスで型なし制約を使用します。
5. ジェネリック クラス
ジェネリック クラスは、特定のデータ型に固有ではない操作をカプセル化します。ジェネリック クラスは、リンク リスト、ハッシュ テーブル、スタック、キュー、ツリーなどのコンテナ クラスでよく使用されます。これらのクラスの操作 (コンテナーへの要素の追加や削除など) は、格納されているデータの種類に関係なく、ほぼ同じ操作を実行します。
通常、既存の具象クラスからジェネリック クラスを作成し、汎用性と使いやすさの最適なバランスが得られるまで、一度に 1 つずつ型を型パラメーターに変更します。独自のジェネリック クラスを作成する場合、考慮すべき重要な点は次のとおりです:
どの型を型パラメーターに一般化する必要があるか。一般的なルールとして、パラメーターで表される型が増えるほど、コードの柔軟性と再利用性が高まります。一般化しすぎると、他の開発者がコードを理解しにくくなる可能性があります。
制約がある場合、型パラメータにどのような制約が必要か。処理する必要があるすべての型を確実に処理できるようにしながら、可能な限り最大の制約を使用することをお勧めします。たとえば、ジェネリック クラスが参照型のみを使用することがわかっている場合は、そのクラスに制約を適用します。これにより、値型の誤った使用が防止され、T で as 演算子を使用して、null 参照をチェックできるようになります。ジェネリック クラスは基本クラスとして使用できます。これは、非ジェネリック クラスの設計でも考慮する必要があります。ジェネリック基本クラスの継承ルール
-
1 つ以上のジェネリック インターフェイスを実装するかどうか。たとえば、ジェネリックス ベースのコンテナーに要素を作成するクラスを設計するには、IComparable のようなインターフェイスを実装するとよいでしょう。ここで、T はクラスのパラメーターです。
ジェネリック クラス Node の場合、クライアント コードは型パラメーターを指定して閉じた構築型 (Node) を作成することも、ジェネリック型 Base を指定するなど、型パラメーターを未指定のままにすることもできます。クラスを使用して、オープンな構築型 (Node) を作成します。ジェネリック クラスは、具象クラス、閉じた構築型、または開いた構築型から継承できます。
// 具象型 class Node : BaseNode// 閉じた構築型class Node ;int>//オープン構築型class Node : BaseNode
非ジェネリック具体クラスは、閉じた構築基本クラスから継承できますが、オープン構築基本クラスからは継承できません。クラス。これは、クライアント コードが基本クラスに必要な型パラメーターを提供できないためです。 T> |
ジェネリック型の具象クラスは、オープン構築型から継承できます。サブクラスと共有される型パラメータを除き、すべての型パラメータに型を指定する必要があります:
//エラーを生成します。 class Node BaseNode . class Node : BaseNode {…}
|
オープン構造型、パラメーターの型、制約を指定する必要があります:
class NodeItem where T : IComparable, new() {…} class MyNodeItem where T : IComparable, new() {…}
ジェネリック型では、さまざまな型パラメーターと制約を使用できます: |
class KeyType {…}class SuperKeyType where U : IComparable V : new() {…}
オープンおよびクローズの構築型はメソッドのパラメータとして使用できます:
void Swap(List list1, List list2) {…} void Swap(List list2) {…}
| 6. 汎用インターフェース
インターフェースを型パラメータの制約として指定する場合、そのインターフェースを実装する型のみを汎用インターフェースとして使用できます。パラメータを入力します。
次のように、型の制約として複数のインターフェイスを指定できます:
class Stack where T : IComparable, IMyStack1{}
| インターフェイスでは、次のように複数の型パラメータを定義できます:
インターフェイスとクラスの継承規則は同じです:
//わかりました。 IMyInterface: IBaseInterface //わかりました。 IMyInterface : IBaseInterface2< U 型クラスは、クラスのパラメータ リストが必要なパラメータをすべて提供している限り、ジェネリックス インターフェイスまたは閉じた構造インターフェイスを実装できます。次のようにインターフェースによって:
//Yeah. | class MyClass : IBaseInterface //Healthy.class MyClass : IBaseInterface
ジェネリック クラス、ジェネリック構造、ジェネリック インターフェイスにはすべて同じメソッド オーバーロード ルールがあります。
7. ジェネリックメソッド |
ジェネリックメソッドは、次のように型パラメータを宣言するメソッドです:
void Swap(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; }
|
次のサンプル コードは、型パラメーターとして int を使用してメソッドを呼び出す例を示しています。
int a = 1;int b = 2;//…Swap(a, b);
型パラメータを無視することもでき、コンパイラは推測してみてください。 Swap を呼び出す次のコードは、上記の例と同等です:
Swap(a, b);
| 静的メソッドとインスタンス メソッドには、同じ型推論ルールがあります。コンパイラは、渡されたメソッド パラメータに基づいて型パラメータを推測できますが、制約や戻り値のみに基づいて決定することはできません。したがって、型推論はパラメータのないメソッドに対しては無効です。型推論は、コンパイラがオーバーロードされたメソッド フラグを解決する前に、コンパイル時に行われます。コンパイラは、同じ名前を持つすべてのジェネリック メソッドに型推論ロジックを適用します。オーバーロードの解決フェーズでは、コンパイラには、型推論が成功したジェネリック クラスのみが含まれます。 ジェネリック クラスでは、非ジェネリック メソッドはクラス内の型パラメーターにアクセスできます:
class List { void Swap(ref T lhs, ref T rhs) { ... } } |
ジェネリック クラスで、そのメソッドが配置されているクラスと同じ型パラメータを持つジェネリック メソッドを定義しようとすると、コンパイラによって警告 CS0693 が生成されます。 。
| class List T>の型パラメータは同じ名前を持っています
ジェネリック クラスで、ジェネリック クラス内の未定義の型パラメーターを定義するためのジェネリック メソッドを定義します。 (一般的には使用されず、制約とともに使用されます)
class List { void Swap< U>(ref T lhs, ref T rhs) { // 一般的には使用されません
void Add(List items) where U : T{} // 一般的に使用されます }
| ジェネリック メソッドは、複数の型パラメーターでオーバーロードされます。たとえば、次のメソッドを同じクラスに配置できます。
void DoSomething() { } void DoSomething() { } void DoSomething() { } |
8. ジェネリックスのデフォルトキーワード
ジェネリッククラスとジェネリックメソッドで発生する問題は、パラメータ化された型にデフォルト値をどのように割り当てるかです。現時点では、それを知ることは不可能です。事前に次の 2 つのことを確認してください:
- T は値型または参照型になります
- T が値型の場合、T は数値または構造体になります
パラメーター化された型の場合 T の変数 t の場合、 t = null ステートメントは T が参照型である場合にのみ有効です。 t = 0 は数値に対してのみ有効であり、構造体に対しては無効です。この問題の解決策は、default キーワードを使用することです。このキーワードは、参照型の場合は null を返し、値の型の場合は 0 を返します。構造体の場合、構造体の各メンバーを返し、メンバーが値型であるか参照型であるかに応じて 0 または null を返します。
|
|
以上がジェネリックの概要と具体的な用途の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。