Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

.method private hidebysig static void Main(string[] args) cil managed {

 …

 box [mscorlib]System.Int32

 callvirt instance int32 [mscorlib] System.Collections.ArrayList::Add(object)

 pop

 ldstr "Значение вашего int: {0}"

 ldloc.0

 ldc.i4.0

 callvirt instance object [mscorlib] System.Collections.ArrayList::get_Item(int32)

 unbox [mscorlib]System.Int32

ldind.i4

 box [mscorlib]System.Int32

call void [mscorlib]System.Console::WriteLine(string, object)

 …

}

Обратите

внимание на то. что перед обращением к ArrayList.Add размещенное в стеке значение System.Int32 преобразуется в объект, чтобы передать требуемый System.Object. Также заметьте, что при чтении из ArrayList с помощью индексатора типа (что отображается в скрытый метод get_Item) объект System.Object восстанавливается в System.Int32 только для того, чтобы снова стать объектным образом при передаче методу Console.WriteLine.

Проблемы создания объектных образов и восстановления значений

Операции создания объектных образов и восстановления из них значений очень удобны с точки зрении программиста, но такой упрощенный подход при обмене элементами стека и динамической памяти имеет свои специфические проблемы производительности и не гарантирует типовой безопасности. Чтобы понять проблемы производительности, рассмотрим следующие шаги, которые приходится выполнять при создании объектного образа и восстановлении значения обычного целого числа.

1. Новый объект нужно разместить в управляемой динамической памяти.

2. Значение размещенных в стеке данных нужно записать в соответствующее место в памяти.

3. При восстановлении значений, сохраненного в объекте, размещенном в динамической памяти, это значение нужно снова вернуть в стек.

4. Неиспользуемый объект в управляемой динамической памяти (в конце концов) должен быть уничтожен сборщиком мусора.

В нашей ситуации метод Main с точки зрения производительности не имеет никаких проблем, но соответствующие проблемы появятся, когда ArrayList будет содержать тысячи целых значений, если вашей программе придется регулярно их обрабатывать.

Теперь рассмотрим проблему отсутствия: типовой безопасности в отношении операции восстановления значений из объектного образа. Вы знаете, что для восстановления значения в рамках синтаксиса C# используется оператор преобразования. Но каким будет это преобразование – успешным или неудачным, – выяснится только в среде выполнения, При попытке восстановить значение в неправильный тип данных вы получите InvalidCastException.

static void Main(string[] args) {

 …

 // Ой! Исключение времени выполнения!

 Console.WriteLine("Значение вашего int: {0}", (short)myInts[0]);

 Console.ReadLine;

}

В идеальной ситуации компилятор C# должен решать проблемы некорректных операций восстановления из объектного образа во время компиляции, а не в среде выполнения. В связи с этим, в действительно идеальной ситуации, можно было бы сохранить

типы, характеризуемые значениями, в контейнере, который не требовал бы преобразования в объект. В .NET 2.0 обобщения дают решение именно этих проблем. Однако перед тем как углубиться в детали использования обобщений, давайте посмотрим, как программисты пытались бороться с этими проблемами в .NET 1.x. с помощью строго типизованных коллекций.

Типовая безопасность и строго типизованные коллекции

В мире .NET, существовавшем до появления версии 2.0, программисты попытались решить проблемы типовой безопасности с помощью построения пользовательских строго типизованных коллекций. Для примера предположим, что вы хотите создать пользовательскую коллекцию, которая сможет содержать только объекты типа Person (персона).

public class Person {

 // Определены открытыми для простоты.

 public int currAge;

 public string fName, lName;

 public Person{}

 public Person(string firstName, string lastName, int age) {

currAge = age;

fName = firstName;

lName = lastName;

 }

 public override string ToString {

return string.Format("Возраст {0}, {1} равен (2}", lName, fName, currAge);

 }

}

Чтобы построить коллекцию персон, можно определить член-переменную

System.Collections.ArrayList в рамках класса PeopleCollection и настроить все члены на работу со строго типизованными объектами Person, а не с общими объектами System.Object.

public class PeopleCollection: IEnumerable {

 private ArrayList arPeople = new ArrayList;

 public PeopleCollection{}

 // Преобразование для вызывающей стороны.

 public Person GetPerson(int pos) { return (Person)arPeople[pos]; }

 // Вставка только типов Person.

 public void AddPerson(Person p) { arPeople.Add(p); }

 public void ClearPeople { arPeople.Clear; }

 public int Count { get { return arPeople.Count; } }

 // Поддержка foreach нумератора.

 IEnumerator IEnumerable.GetEnumerator { return arPeople.GetEnumerator; }

}

С такими определениями типов вы будете уверены в типовой безопасности, поскольку теперь компилятор C# сможет распознать любую попытку вставки неподходящего типа,

static void Main (string[] args) {

 Console.WriteLine("***** Custom Person Collection *****\n");

 PeopleCollection myPeople = new PeopleCollection;

 myPeople.AddPerson(new Person("Homer", "Simpson", 40));

 myPeople.AddPerson(new Person("Marge", "Simpson", 38));

Поделиться с друзьями: