Проблемы создания объектных образов и строго типизованные коллекции
Проблемы создания объектных образов и строго типизованные коллекции
Строго типизованные коллекции можно найти в библиотеках базовых классов .NET и это очень полезные программные конструкции. Однако эти пользовательские контейнеры мало помотают в решении проблем создания объектных образов. Даже если вы создадите пользовательскую коллекцию с именем IntCollection, предназначенную для работы только с типами данных System.Int32, вам придется создать объект некоторого типа для хранения самих данных (System.Array, System.Collections.ArrayList и т.п.).
public class IntCollection: IEnumerable {
private ArrayList arInts = new ArrayList();
public IntCollection() {}
// Восстановление значения для вызывающей стороны.
public int GetInt(int pos) { return (int)arInts[pos]; }
// Операция создания объектного образа!
public void AddInt(int i) { arInts.Add(i); }
public void ClearInts() { arInts.Clear(); }
public int Count { get { return arInts.Count; } }
IEnumerator IEnumerable.GetEnumerator() { return arInts.GetEnumerator(); }
}
Вне зависимости от того, какой тип вы выберете для хранения целых чисел (System.Array, System.Collections.ArrayList и т.п.), вы не сможете избавиться от проблемы .NET 1.1, связанной с созданием объектных образов. Нетрудно догадаться, что здесь снова на помощь приходят обобщения. В следующем фрагменте программного кода тип System.Collections.Generic.List‹› используется для создания контейнера целых чисел, не имеющего проблем создания объектных образов и восстановлений значений при вставке и получении типов характеризуемых значений.
static void Main (string [] args) {
// Баз создания объектного образа!
List‹int› myInts = new List‹int›();
myInts.Add.(5);
// Без восстановления значения!
int i = myInts[0];
}
Просто в качестве подтверждения рассмотрите следующий CIL-код для этого метода Main() (обратите внимание да отсутствие в нем каких бы то ни было блоков box и unbox).
.method private hidebysig static void Main(string[] args) cil managed {
.entrypoint
// Code size 24 (0x18)
.maxstack 2
.locals init ([0] class [mscorlib] System.Collections.Generic.List`1‹int32› myInts, [1] int32 i)
IL_0000: nop
IL_0001: newobj instance void class [mscorlib] System.Collections.Generic.List`1‹int32›::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.5
IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1‹int32›::Add(!0)
IL_000e: nop
IL_000f: ldloc.0
IL_0010: ldc.i4.0
IL_0011: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1‹int32›::get_Item(int32)
IL_0016: stloc.1
IL_0017: ret
} // end of method Program::Main
Теперь, когда вы имеете лучшее представление о роли обобщений в .NET2.0, мы с вами готовы углубиться в детали. Для начала мы формально рассмотрим пространство имен System.Collections.Generic.
Исходный код. Проект CustomNonGenericCollection размещен в подкаталоге, соответствующем главе 10.