More Effective C#:汎用の使用
7419 ワード
.NET 2.0が追加した汎用型から,開発者がコードを書く方法と方式に大きく影響した.汎用型は集合にのみ適用されるだけでなく,インタフェース,抽出アルゴリズムなどにも大きな影響を及ぼす.
汎用クラス定義はMSILタイプとして完全にコンパイルでき、制約を満たすタイプパラメータに対して、汎用タイプに含まれるコードは完全に合法であることを保証しなければならない.すべてのタイプのパラメータが明確に与えられた汎用タイプを閉鎖汎用タイプと呼び,一部のタイプのパラメータのみを与えた汎用タイプを開放汎用タイプと呼ぶ.
単純に汎用型を使用するとプログラムのサイズが小さくなり、正しくありません.プログラムのサイズに影響する要因には、プログラムで使用されるタイプパラメータの個数と、作成された閉じた汎用型の個数が含まれます.
ILにおける汎用型は,ある実タイプ定義の一部と見なすことができる.ILは、完全な汎用タイプインスタンスを初期化するためにプレースホルダを予約した.JITコンパイラは、実行時にマシンコードを生成する際に、この閉じた汎用タイプの定義を補完します.このような処理方法は、複数の閉じた汎用タイプが処理コードのオーバーヘッドを増大させ、データを格納する時間/空間オーバーヘッドが減少するという利点において、矛盾をもたらす.
異なる閉じた汎用タイプは、コードが異なる最終ランタイム形式を生成する可能性があります.複数の閉じた汎用タイプを作成すると、JITコンパイラとCLRが最適化され、メモリへの圧力が低下します.IL形式のプログラムセットはメモリのデータページにロードされ、JITコンパイラがILコードをマシン命令に変換した後にのみ、生成されたマシンコードは読み取り専用のコードページに配置される.
汎用型かどうかにかかわらず、各タイプは上記の手順を実行し、非汎用型の場合、クラスのILコードとそれが生成するマシンコードとの間には一対一の関係があり、汎用型の出現は変換の過程をやや複雑にし、JITが汎用型クラスを変換する際に、JITコンパイラは現在のタイプパラメータをチェックし、その情報に基づいて特定の命令を生成する.JITコンパイラは、異なるタイプのパラメータが同じマシンコードを使用できるように、このプロセスを一連の最適化します.
参照タイプ生成の汎用クラスでは、JITコンパイラが一意のマシンコードバージョンを生成します.次のコードを見てください.
上記のコードは、運転時のマシンコードと全く同じです.
値タイプによって生成される汎用クラスでは、JITコンパイラが異なるタイプのパラメータに対して異なるバージョンのマシンコードを作成します.次のコードを見てください.
上記のコードは、実行時のマシンコードが異なります.
実行時にJITが1つの汎用定義をコンパイルする必要があり、少なくとも1つのタイプのパラメータが値タイプである場合、このプロセスは2つのステップに分けることができる:1.コンパイラは、閉じた汎用タイプを表す新しいILクラスを作成する.2.JITはこのコードをX 86命令にコンパイルする.この2つのステップは、JITがクラスのロード時に完全なX 86命令を生成するのではなく、クラス内の各メソッドが最初に呼び出されたときにのみコンパイルを開始するため、非常に必要である.このように,フレームワークはILコード上で置換のステップを先に実行し,その後,通常のクラス定義のようにオンデマンドでコンパイルする必要がある.
これは、実行時の追加メモリ使用量が、1.値タイプをパラメータとする閉じた汎用タイプごとにIL定義のコピーを保存する2つの部分に分けられることを意味します.2.呼び出したメソッドのマシンコードのコピーを、値タイプをパラメータとして使用する閉じたタイプごとに保存します.
汎用クラス定義はMSILタイプとして完全にコンパイルでき、制約を満たすタイプパラメータに対して、汎用タイプに含まれるコードは完全に合法であることを保証しなければならない.すべてのタイプのパラメータが明確に与えられた汎用タイプを閉鎖汎用タイプと呼び,一部のタイプのパラメータのみを与えた汎用タイプを開放汎用タイプと呼ぶ.
単純に汎用型を使用するとプログラムのサイズが小さくなり、正しくありません.プログラムのサイズに影響する要因には、プログラムで使用されるタイプパラメータの個数と、作成された閉じた汎用型の個数が含まれます.
ILにおける汎用型は,ある実タイプ定義の一部と見なすことができる.ILは、完全な汎用タイプインスタンスを初期化するためにプレースホルダを予約した.JITコンパイラは、実行時にマシンコードを生成する際に、この閉じた汎用タイプの定義を補完します.このような処理方法は、複数の閉じた汎用タイプが処理コードのオーバーヘッドを増大させ、データを格納する時間/空間オーバーヘッドが減少するという利点において、矛盾をもたらす.
異なる閉じた汎用タイプは、コードが異なる最終ランタイム形式を生成する可能性があります.複数の閉じた汎用タイプを作成すると、JITコンパイラとCLRが最適化され、メモリへの圧力が低下します.IL形式のプログラムセットはメモリのデータページにロードされ、JITコンパイラがILコードをマシン命令に変換した後にのみ、生成されたマシンコードは読み取り専用のコードページに配置される.
汎用型かどうかにかかわらず、各タイプは上記の手順を実行し、非汎用型の場合、クラスのILコードとそれが生成するマシンコードとの間には一対一の関係があり、汎用型の出現は変換の過程をやや複雑にし、JITが汎用型クラスを変換する際に、JITコンパイラは現在のタイプパラメータをチェックし、その情報に基づいて特定の命令を生成する.JITコンパイラは、異なるタイプのパラメータが同じマシンコードを使用できるように、このプロセスを一連の最適化します.
参照タイプ生成の汎用クラスでは、JITコンパイラが一意のマシンコードバージョンを生成します.次のコードを見てください.
1
private
static
void
InitRefTypeGen()
2
{
3
List
<
string
>
stringList
=
new
List
<
string
>
();
4
List
<
Stream
>
openFiles
=
new
List
<
Stream
>
();
5
List
<
Employee
>
empList
=
new
List
<
Employee
>
();
6
}
上記のコードは、運転時のマシンコードと全く同じです.
値タイプによって生成される汎用クラスでは、JITコンパイラが異なるタイプのパラメータに対して異なるバージョンのマシンコードを作成します.次のコードを見てください.
1
private
static
void
InitValTypeGen()
2
{
3
List
<
double
>
doubleList
=
new
List
<
double
>
();
4
List
<
int
>
intList
=
new
List
<
int
>
();
5
List
<
Name
>
nameList
=
new
List
<
Name
>
();
6
}
上記のコードは、実行時のマシンコードが異なります.
実行時にJITが1つの汎用定義をコンパイルする必要があり、少なくとも1つのタイプのパラメータが値タイプである場合、このプロセスは2つのステップに分けることができる:1.コンパイラは、閉じた汎用タイプを表す新しいILクラスを作成する.2.JITはこのコードをX 86命令にコンパイルする.この2つのステップは、JITがクラスのロード時に完全なX 86命令を生成するのではなく、クラス内の各メソッドが最初に呼び出されたときにのみコンパイルを開始するため、非常に必要である.このように,フレームワークはILコード上で置換のステップを先に実行し,その後,通常のクラス定義のようにオンデマンドでコンパイルする必要がある.
これは、実行時の追加メモリ使用量が、1.値タイプをパラメータとする閉じた汎用タイプごとにIL定義のコピーを保存する2つの部分に分けられることを意味します.2.呼び出したメソッドのマシンコードのコピーを、値タイプをパラメータとして使用する閉じたタイプごとに保存します.