ジェネリック系のコレクションを紹介しましたが、実は非ジェネリックなコレクションも存在します。
これはC#にジェネリックが実装される前に存在したクラスで、現在は全く使われてません。
つまり、これらのクラスが利用されてるコードは総じてゴミです。江戸時代で時が止まってます。
と言うことで、そんな使わないクラスを把握しておき、ゴミを増やさないように気をつけましょう。
今回紹介するクラスはマジでメリットがありません。素直にジェネリック版を使いましょう。
ここでは.NETに定義されたジェネリック型を実際に利用してみましょう。なお、利用することが目的なので、1つ1つのジェネリック型については深く解説しません。 System.Collections.Generic名前空間 コレクション系のジェネリック型が定義されてる空間です。C#でジェネリック型と言えば、ここです。と言っても、ジェネリック型はあくまで総称なので、他の名前空間にも色々と定義されてます。 この名前空間の中にも複数のジェネリック型が存在しますが、今回はList型と...
System.Collections名前空間
ここに存在するのが非ジェネリック系のコレクションです。
ジェネリック版と同じような名前をしてますが、完全にはリンクしてません。
System.Collections | System.Collections.Generic |
---|---|
ArrayList | List<T> |
Hashtable | Dictionary<TKey,TValue> |
Stack | Stack<T> |
Queue | Queue<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型になるため、取り出すときにダウンキャストが必要になります。
これらの理由から使い勝手が悪く、ジェネリック型と比べてメリットがありません。ついでにボックス化の影響も受けます。
ここではC#のボックス化(Boxing)について学習します。C#には大きく値型と参照型がありますが、これに関連する話です。 ボックス化(Boxing)とボックス化の解除(Unboxing) ボックス化とは、値型を参照型にキャストすることを言います。ボックス化の解除は、その逆で参照型を値型にキャストすることです。 これ、単にキャストだけの話ではありません。 C#は全ての型が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#に関する学習コンテンツ
この記事は参考になりましたか?
コメント