開発者は、暗黙的でも明示的でも、常に .NET でジェネリックを使用します。 .NET で LINQ を使用する場合、 IEnumerable<T>を使用していることに気付いたことがありますか? または、Entity Framework を使用してデータベースと通信するための "汎用リポジトリ" のオンライン サンプルを見たことがある場合、ほとんどのメソッドが IQueryable<T>
を返すことがわかりますか? あなたは、これらの例で T が何であるか、なぜそこにあるのか疑問に思ったかもしれません。
.NET Framework 2.0 で初めて導入されたジェネリックは、基本的に、開発者が実際のデータ型にコミットすることなく 、型セーフな データ構造を定義できる "コード テンプレート" です。 たとえば、List<T>は、、List<int>
、List<string>
など、任意の型で宣言して使用できるList<Person>
です。
ジェネリックが便利な理由を理解するために、ジェネリックを追加する前と後の特定のクラスを見てみましょう: ArrayList。 .NET Framework 1.0 では、 ArrayList
要素は Object型でした。 コレクションに追加された要素は、サイレントモードで Object
に変換されました。 リストから要素を読み取る場合も同じことが起こります。 このプロセスは ボックス化とボックス化解除と呼ばれ、パフォーマンスに影響します。 ただし、パフォーマンスとは別に、コンパイル時にリスト内のデータの種類を決定する方法はありません。これにより、コードが脆弱になります。 ジェネリックは、リストの各インスタンスに含まれるデータの種類を定義することで、この問題を解決します。 たとえば、整数のみをList<int>
に追加し、人物のみをList<Person>
に追加できます。
ジェネリックは実行時にも使用できます。 ランタイムは、使用しているデータ構造の種類を認識し、より効率的にメモリに格納できます。
次の例は、実行時にデータ構造の種類を把握する効率を示す小さなプログラムです。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
//timer for non-generic list sort
Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}
このプログラムでは、次のような出力が生成されます。
Generic Sort: System.Collections.Generic.List`1[System.Int32]
Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms
ここで最初に気付くことができるのは、ジェネリック リストを並べ替える方が、非ジェネリック リストの並べ替えよりも大幅に高速であるということです。 また、ジェネリック リストの型が異なる ([System.Int32])、非ジェネリック リストの型が一般化されていることにも気付く場合があります。 ランタイムはジェネリック List<int>
が Int32型であることを認識しているため、基になる整数配列内のリスト要素をメモリに格納できますが、非ジェネリック ArrayList
は各リスト要素をオブジェクトにキャストする必要があります。 この例に示すように、余分なキャストは時間がかかり、リストの並べ替えが遅くなります。
ジェネリックの型を知っているランタイムの追加の利点は、デバッグ エクスペリエンスが向上することです。 C# でジェネリックをデバッグする場合、データ構造内の各要素の型がわかります。 ジェネリックがないと、各要素がどのような型だったのか分かりません。
こちらも参照ください
.NET