なぜ C# ジェネリックを使用するのですか?
この問題を理解するために、まず次のコードを見てみましょう。このコードでは一部の内容が省略されていますが、このスタックは int データ型のみを処理できます。 class Stack PRivate int[] m_item; public int Pop(){...} public void Push(int item){...} public Stack(int i) { this.m_item = new int[i]それは素晴らしいことですが、文字列型を保存するためにスタックが必要な場合はどうすればよいでしょうか?多くの人は上記のコードをコピーして int を string に変更することを考えるでしょう。もちろん、これを行うこと自体に問題はありませんが、優れたプログラムは、将来ロングまたは Node 型のスタックが必要になった場合にどうするかを考えるため、これを実行しません。もう一度コピーしますか?優れたプログラマーは、共通のデータ型オブジェクトを使用してこのスタックを実装することを考えます。しかし、総合的に見て、欠陥がないわけではありません。
スタックが値の型を処理するとき、ボックス化およびフォールディング操作が発生し、データ量が多ければ、マネージド ヒープ上に多数の変数が割り当てられ、再利用されます。が大きい場合、パフォーマンスの低下は非常に深刻です。
参照型を扱う場合、ボックス化やフォールディング操作はありませんが、データ型強制操作が使用されるため、プロセッサの負荷が増加します。
データ型強制にはさらに深刻な問題があります (スタックが Stack のインスタンスであると仮定します):
Node1 x = new Node1(); Pop(); 上記のコードはコンパイル時にはまったく問題ありませんが、Node1 型のデータがはプッシュされますが、Pop 中に Node2 型に変換する必要があるため、プログラムの実行時に型変換例外が発生しますが、コンパイラ チェックはエスケープされません。
オブジェクト型スタックの問題を解決するために、これらの問題をエレガントに解決できるジェネリックスを導入します。ジェネリックスは、オブジェクトの代わりに渡されたデータ型 T を使用します。T の型は、クラスがインスタンス化されるときに指定され、実行効率とコードの品質が大幅に向上し、データ型が保証されます。 。 安全性。
C# ジェネリックの使用
以下は、ジェネリックを使用して上記のスタックを書き換えます。共通のデータ型 T をプレースホルダーとして使用し、インスタンス化中に実際の型に置き換えられるのを待ちます。ジェネリックの威力を見てみましょう:
Public class stack {Private T [] m_item; Public T POP () {...} Public Void Push (t item) {...} Public Stack (int i) { This.m_item = new T[i]; クラスの記述方法は変更されていません。これは一般的なデータ型 T を導入するだけであり、任意のデータ型に適用でき、タイプ セーフです。このクラスのメソッド呼び出し:
//インスタンス化は int 型のクラスのみを保存できます。 Stack a = new Stack(10); a.Push("8888"); //クラス a は int 型のみを受け取るため、この行はコンパイルされません。 data int x = a.Pop(); //インスタンス化は文字列型のクラスのみを保存できます Stack b = new Stack(100); //クラス b は文字列型のみを受け取るため、この行はコンパイルされません。 data b.Push("8888"); string y = b.Pop(); このクラスは、オブジェクト
1 によって実装されたクラスとは完全に異なります。 int 型のスタックがインスタンス化される場合、string 型のデータは処理できません。他のデータ型についても同様です。
2. 箱を梱包したり折りたたんだりする必要はありません。このクラスがインスタンス化されると、渡されたデータ型に従ってローカル コードが生成されます。ローカル コードのデータ型は決定されているため、ボックス化やフォールディングは必要ありません。
3. 型変換は必要ありません。
理論的知識:
いわゆるジェネリックス: パラメーター化された型を使用して、同じコード上で複数のデータ型を操作します。ジェネリック プログラミングは、「パラメータ化された型」を使用して型を抽象化し、より柔軟な再利用を実現するプログラミング パラダイムです。
C# ジェネリックは、コードに強力な型安全性、より優れた再利用、より高い効率、より明確な制約を与えます。
C# の汎用機能は実行時に CLR によってサポートされます。これは、C++ のコンパイル時のテンプレート メカニズムや Java のコンパイル時の「ワイピング メソッド」とは異なります。これにより、汎用機能が CLR 対応言語間でシームレスに相互運用できるようになります。
C# ジェネリック コードは、IL およびメタデータにコンパイルされるときに特別なプレースホルダーを使用してジェネリック型を表し、独自の IL 命令を使用してジェネリック操作をサポートします。実際の汎用インスタンス化作業は、JIT コンパイル中に「オンデマンド」方式で発生します。
C# のジェネリック コンパイル メカニズムは次のとおりです。
コンパイルの最初のラウンドでは、コンパイラーはスタック型の IL コードとメタデータの「ジェネリック バージョン」のみを生成し、ジェネリック型 T のみをインスタンス化します。プレースホルダーとして機能します。
JIT コンパイル中に、JIT コンパイラーが初めて Stack に遭遇すると、「ジェネリック バージョン」の IL コードとメタデータの T を int 型に置き換えて、ジェネリック型をインスタンス化します。
CLR は、型パラメーターが「参照型」であるすべてのジェネリック型に対して同じコードを生成しますが、型パラメーターが「値型」の場合、CLR は異なる「値型」コードごとに個別のコードを生成します。
C# ジェネリックのいくつかの機能
インスタンス化されたジェネリック型のパラメーターが同じ場合、JIT コンパイラーはその型を再利用するため、C# の動的ジェネリック機能により、C++ の静的テンプレートによって引き起こされる可能性のあるコードの肥大化の問題が回避されます。
C# のジェネリック型には豊富なメタデータが含まれるため、C# のジェネリック型を強力なリフレクション テクノロジに適用できます。
C# のジェネリックスは、「基本クラス、インターフェイス、コンストラクター、値の型/参照型」の制約メソッドを使用して、型パラメーターに「明示的な制約」を実装します。これにより、型の安全性が向上し、また、以下に基づく C++ テンプレートの「明示的な制約」が失われます。 「署名」の暗黙の制約の高い柔軟性。
C# ジェネリック クラスがコンパイルされると、中間コード IL が最初に生成され、一般的な型 T は単なるプレースホルダーです。クラスをインスタンス化するときに、T はユーザーが指定したデータ型に置き換えられ、ローカル コードがジャストインタイム コンパイラ (JIT) によって生成されます。実際のデータ型はこのローカル コードで使用されており、これは次と同等です。実際の型で書かれたクラスなので異なります。クローズドクラスのネイティブコードは異なります。この原則によれば、ジェネリック クラスの異なるクローズド クラスは異なるデータ型であると考えることができます。
このように、ジェネリックは柔軟性が高まるだけでなく、コードの単純さと単純さを新しいレベルに改善します。さまざまなオーバーロードされたメソッドに対して特定のコードを記述する必要はもうありません。
C# ジェネリックは、開発ツールの中で非常に貴重な資産です。これらはすべて、エレガントで読みやすい構文を通じて、パフォーマンス、タイプ セーフ、品質を向上させ、反復的なプログラミング タスクを削減し、プログラミング モデル全体を簡素化します。 C# ジェネリックのルートは C++ テンプレートですが、C# はコンパイル時の安全性とサポートを提供することでジェネリックを次のレベルに引き上げます。 C# は、2 フェーズ コンパイル、メタデータ、および制約や一般的なメソッドなどの革新的な概念を利用します。 C# の将来のバージョンでは、新しい機能を追加し、データ アクセスやローカリゼーションなどの他の .NET Framework 領域にジェネリックを拡張するために、ジェネリックが進化し続けることは間違いありません
以上が C# ジェネリックの簡単な紹介です。その他の関連コンテンツについては、PHP 中国語 Web サイト (m.sbmmt.com) に注目してください。