非ジェネリック(generic)コレクションについて

[C#] 非ジェネリック(generic)コレクションについて

※ 当サイトは広告を含みます。

ジェネリック系のコレクションを紹介しましたが、実は非ジェネリックなコレクションも存在します。

これはC#にジェネリックが実装される前に存在したクラスで、現在は全く使われてません。
つまり、これらのクラスが利用されてるコードは総じてゴミです。江戸時代で時が止まってます。
と言うことで、そんな使わないクラスを把握しておき、ゴミを増やさないように気をつけましょう。

管理人

今回紹介するクラスはマジでメリットがありません。素直にジェネリック版を使いましょう。

System.Collections名前空間

ここに存在するのが非ジェネリック系のコレクションです。
ジェネリック版と同じような名前をしてますが、完全にはリンクしてません。

System.CollectionsSystem.Collections.Generic
ArrayListList<T>
HashtableDictionary<TKey,TValue>
StackStack<T>
QueueQueue<T>

公式ドキュメントhttps://learn.microsoft.com/en-us/dotnet/api/system.collections

BitArray

System.Collectionsに残された最後の希望です(適当)。
これだけは代替が無いので使うことがあるとかないとか。

りさ

なんで曖昧なんですか?

管理人

僕は使わないから。普通にunsigned型bit演算すればよくね?

公式ドキュメントhttps://learn.microsoft.com/en-us/dotnet/api/system.collections.bitarray

非ジェネリックなコレクションの使い方

ArrayListを例にして非ジェネリックなコレクションを使ってみましょう。
やってることは難しくないのですが、キャストが果てしなく面倒です。


namespace Sample
{
  internal class Program
  {
    static void Main(string[] args)
    {
      var arrayList = new System.Collections.ArrayList();

      // ArrayListにアイテムを追加する
      arrayList.Add(1);
      arrayList.Add(2);
      arrayList.Add(3);
      arrayList.Add(4);
      arrayList.Add(5);

      // ArrayListにforでアクセスする
      Console.WriteLine("----- for -----");
      for (int i = 0; i < arrayList.Count; i++)
        Console.WriteLine($"arrayList[{i}]={(int)arrayList[i]}");

      // ArrayListの0番目を削除する
      arrayList.RemoveAt(0);

      // ArrayListの0番目に値を追加する
      arrayList.Insert(0, 10);

      // ArrayListにforeachでアクセスする
      Console.WriteLine("----- foreach -----");
      foreach (int item in arrayList) // int型を指定
        Console.WriteLine($"item={item}");

      // ArrayListの中身を全て消す
      arrayList.Clear();

      // ArrayListにforeachでアクセスする
      Console.WriteLine("----- foreach -----");
      foreach (var item in arrayList) // var型を指定
        Console.WriteLine($"item={(int)item}");
    }
  }
}

ポイントは全てのデータ型をobject型として扱う部分です。

C#は全ての型がobject型を継承してるため、必然的にobject型アップキャストできます。
この機能を利用して汎用化してるのが非ジェネリックなコレクションです。

つまり、利用する場合はint型でもstring型でも、全部object型にキャストして使います。
当然コレクションに格納された後はobject型になるため、取り出すときにダウンキャストが必要になります。
これらの理由から使い勝手が悪く、ジェネリック型と比べてメリットがありません。ついでにボックス化の影響も受けます。

なお、上記の例に限って言えば、ToString()が機能するのでキャストしなくても動きます。
ただし、現実的な利用方法においてはキャストを多用する必要があり、手間しかありません。

ジェネリック型を非ジェネリック型のように使う方法

1億歩譲って、どんなデータも格納できることをメリットと言うなら、ジェネリック型でも同じことができます。
方法も簡単で、List<T>型object型を指定してList<object>型にします。もしくはdynamic型を使います。


namespace Sample
{
  internal class Program
  {
    static void Main(string[] args)
    {
      var arrayList = new System.Collections.Generic.List<object>();

      // ArrayListにアイテムを追加する
      arrayList.Add(1);
      arrayList.Add(2);
      arrayList.Add(3);
      arrayList.Add(4);
      arrayList.Add(5);

      // ArrayListにforでアクセスする
      Console.WriteLine("----- for -----");
      for (int i = 0; i < arrayList.Count; i++)
        Console.WriteLine($"arrayList[{i}]={(int)arrayList[i]}");

      // ArrayListの0番目を削除する
      arrayList.RemoveAt(0);

      // ArrayListの0番目に値を追加する
      arrayList.Insert(0, 10);

      // ArrayListにforeachでアクセスする
      Console.WriteLine("----- foreach -----");
      foreach (int item in arrayList) // int型を指定
        Console.WriteLine($"item={item}");

      // ArrayListの中身を全て消す
      arrayList.Clear();

      // ArrayListにforeachでアクセスする
      Console.WriteLine("----- foreach -----");
      foreach (var item in arrayList) // var型を指定
        Console.WriteLine($"item={(int)item}");
    }
  }
}

正直、メリットはありません。普通なら継承関係を作って上位クラスをT型に指定します。
と言うか、こんな実装が必要なら根本的に設計が間違ってます。早く正気になりましょう。

あとがき

残ってる理由は互換性です。いきなり消すと利用者がビルドエラーになるのでMicrosoft様が善意で残してくれてます。
ぶっちゃけ、僕がC#に出会った時点でジェネリック型だったので、非ジェネリックって相当古いと思います。

りさ

そんなに古いなら消せばいいのに。

管理人

.NET Frameworkから.NETに移行した時点で消すべきだった説。

◆ C#に関する学習コンテンツ

この記事は参考になりましたか?

関連記事

コメント

この記事へのコメントはありません。