次の方法で共有


ジェネリック型の概要

開発者は、暗黙的でも明示的でも、常に .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# でジェネリックをデバッグする場合、データ構造内の各要素の型がわかります。 ジェネリックがないと、各要素がどのような型だったのか分かりません。

こちらも参照ください