Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы ОСиСП.doc
Скачиваний:
68
Добавлен:
11.05.2015
Размер:
1.78 Mб
Скачать

Ограничения конструктора

В параметре-типе можно задавать не более одного ограничения конструктора. Ограничение конструктора указывает компилятору, что указанный аргумент-тип будет неабстрактного типа, реализующего открытый конструктор без параметров. Заметьте: компилятор C# считает за ошибку одновременное задание ограничения конструктора и ограничения struct, потому что это избыточно. У всех значимых типов неявно присутствует открытый конструктор без параметров. В следующем классе для параметров-типов использовано ограничение конструктора.

internal sealed class ConstructorConstraint<T> where T : new()

{

public static T Factory()

{

// Допустимо, потому что у всех значимых типов неявно

// есть открытый конструктор без параметров и потому что

// это ограничение требует, чтобы у всех указанных ссылочных типов

// также был открытый конструктор без параметров.

return new Т();

}

}

В этом примере применение оператора new по отношению к Tдопустимо, потому что известно, чтоT— это тип с открытым конструктором без параметров. Разумеется, это справедливо и для всех значимых типов, а ограничение конструктора требует, чтобы это условие выполнялось и для всех ссылочных типов, заданных как аргумент-тип.

Иногда разработчики предпочитают объявлять параметр-тип с помощью ограничения конструктора, при котором сам конструктор принимает различные параметры. На сегодняшний день CLR (и, как следствие, компилятор C#) поддерживают только конструкторы без параметров. По мнению специалистов компании Microsoft, в большинстве случаев этого вполне достаточно, и я с ними полностью согласен.

Другие вопросы верификации

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

Приведение переменной обобщенного типа

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

private static void CastingAGenericTypeVariable<T>(T obj)

{

Int32 x = (Int32)obj; // Ошибка.

String s = (String)obj; // Ошибка.

}

Компилятор вернет ошибку для обеих строк, потому что Tможет быть любого типа и успех приведения типов не гарантирован. Чтобы этот код скомпилировался, его нужно изменить, добавив в начале приведение к Object:

private static void CastingAGenericTypeVariable2<T>(T obj)

{

Int32 x = (Int32)(Object)obj; // Ошибки нет.

String s = (String) (Object) obj; // Ошибки нет.

}

Теперь этот код скомпилируется, но во время выполнения CLR все равно может сгенерировать исключение InvalidCastException.

Для приведения к ссылочному типу также используется оператор as языка C#. В следующем коде он используется с типом String (поскольку Int32 — значимый тип).

private static void CastingAGenericTypeVariable3<T>(T obj)

{

String s = obj as String; // Ошибки нет.

}

Присвоение переменной обобщенного типа значения по умолчанию

Приравнивание переменной обобщенного типа к null допустимо, только если обобщенный тип ограничен ссылочным типом.

private static void SettingAGenericTypeVariableToNull<T>()

{

T temp = null;

// CS0403 - Cannot convert null to type parameter 'T'

// because it could be a value type...

// Ошибка CS0403 - нельзя преобразовать null в параметр-тип T,

// потому что T может быть значимого типа.

}

Так как Tне ограничен, он может быть значимого типа, а приравнять переменную значимого типа к null нельзя. Если жеTбыл бы ограничен ссылочным типом, temp можно было бы приравнять к null, и код скомпилировался бы и работал.

При создании C# в Microsoft посчитали, что разработчикам может понадобиться присвоить переменной значение по умолчанию. Для этого в компилятореC# есть ключевое словоdefault.

private static void SettingAGenericTypeVariableToDefaultValue<T>()

{

T temp = default(T); // Работает.

}

В этом примере ключевое слово default дает команду компилятору C# и JIT-компилятору CLR создать код, приравнивающий temp к null, еслиT— ссылочного типа, и обнуляющий все биты переменной temp, если T — значимого типа.