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

Сравнение переменной обобщенного типа с null

Сравнение переменной обобщенного типа с null с помощью операторов «==» и «!=» допустимо, не зависимо от того, ограничен обобщенный тип или нет.

private static void ComparingAGenericTypeVariableWithNull<T>(T obj)

{

if (obj == null)

{

/* Этот код никогда не исполнится, если тип - значимый */

}

}

Так как T не ограничен, он может быть ссылочного или значимого типа. Во втором случае obj нельзя приравнять null. Обычно в таком случае компилятор C# должен выдать ошибку. Но этого не происходит — код успешно компилируется. При вызове этого метода с использованием аргумента-типа значимого типа JIT-компилятор, обнаружив, что выражение г/никогда не равно true, просто не создаст машинный код для оператора if и кода в фигурных скобках. Если бы я использовал оператор «!=», JIT-компилятор также не сгенерировал бы код для оператора if (поскольку его значение всегда true), но сгенерировал бы код из фигурных скобок после if.

Кстати, если к Tприменить ограничение struct, компиляторC# не вернет ошибку, потому что не нужно создавать код, сравнивающий значимый тип с null, — результат всегда один.

Сравнение двух переменных обобщенного типа

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

private static void ComparingTwoGenericTypeVariables<T>(T o1, T o2)

{

if (o1 == o2) { } // Ошибка.

}

В этом примере у Tнет ограничений, и, хотя можно сравнивать две переменные ссылочного типа, сравнивать две переменные значимого типа допустимо лишь в том случае, когда значимый тип перегружает оператор ==.

Если у Tесть ограничение class, этот код скомпилируется, а оператор = вернет значение true, если переменные ссылаются на один объект и полностью тождественны. Заметьте: если Т ограничен ссылочным типом, перегружающим метод operator ==, компилятор сгенерирует вызовы этого метода при виде оператора ==. Ясно, что все вышесказанное относится и к оператору !=.

При написании кода для сравнения элементарных значимых типов (Byte, Int32 и так далее) компилятор C# сгенерирует код правильно, а для других значимых типов генерировать код для сравнений он не умеет. Поэтому, если у T метода ComparingTwoGenericTypeVariables есть ограничение struct, компилятор выдаст ошибку. А ограничивать параметр-тип значимым типом нельзя, потому что они неявно являются изолированными. Теоретически этот метод можно скомпилировать, задав в качестве ограничения конкретный значимый тип, но в таком случае метод уже не будет обобщенным. Он будет привязан к конкретному типу данных, и, конечно, компилятор не скомпилирует обобщенный метод, ограниченный одним типом.

Использование переменных обобщенного типа в качестве операндов

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

private static T Sum<T>(T num) where T : struct

{

T sum = default(T);

for (T n = default(T); n < num; n++)

sum += n; return sum;

}

Я также сделал все возможное, чтобы он скомпилировался: определил ограничение struct для T и использовал default(T), чтобы sumиnинициализировались нулем. Но при компиляции кода появились три сообщения об ошибке:

  1. ошибка CS0019: оператор «<» нельзя применять к операндам типаTиT

  2. ошибка CS0023: оператор «++» нельзя применять к операнду типаT

  3. ошибка CS0019: оператор «+=» нельзя применять к операндам типаTиT

Это существенно ограничивает поддержку обобщений в CLR-среде, и многие разработчики испытали глубокое разочарование. Многие пытались создать методы, призванные обойти это ограничение с помощью отражения (главу глава 22), перегрузку оператора и т. п. Но все эти решения сильно снижают производительность или ухудшают читабельность кода.

=====================

Общие (или параметризованные) типы (generics) позволяют при описании классов, структур, методов и интерфейсов использовать параметризованные параметры (не указывать тип параметра в момент написания кода). Тип параметра определяется в момент объявления переменной соответствующего типа. Таким образом можно создать некоторый общий элемент, тип который можно использовать в дальнейшем для данных различных типов. Программисты на C++ могут углядеть с общих типах сходство с шаблонами (templates), в чем-то эта аналогия будет верна, но тут существуют некоторые ограничения.

Создадим класс, используя общие типы:

class Generics<TYPE1, TYPE2>

{

private TYPE1 mVar1;

private TYPE2 mVar2;

public Generics(TYPE1 Var1, TYPE2 Var2)

{

this.mVar1 = Var1;

this.mVar2 = Var2;

}

public string ToStringFunction(string Delemiter)

{

return this.mVar1.ToString() + Delemiter + this.mVar2.ToString();

}

public TYPE1 Variable1

{

get

{

return this.mVar1;

}

set

{

this.mVar1 = value;

}

}

public TYPE2 Variable2

{

get

{

return this.mVar2;

}

set

{

this.mVar2 = value;

}

}

}

Как видно из примера, для того чтобы использовать общие типы нужно после объявления класса указать параметризованные типы: Generics<TYPE1, TYPE2> объявляет класс с двумя параметризованными типами. Теперь используем написанный класс:

// объявление

Generics<string, string> strGeneric = new Generics<string, string>("Hello", "world");

Generics<int, int> intGeneric = new Generics<int, int>(1, 2);

Generics<string, int> strintGeneric = new Generics<string, int>("Three", 3);

int intSum;

string strSum;

// использование

intSum = intGeneric.Variable1 + intGeneric.Variable2;

strSum = strintGeneric.Variable1 + " " + strintGeneric.Variable2.ToString();

MessageBox.Show("\nstrGeneric:\n" + strGeneric.Variable1 + " " + strGeneric.Variable2 +

"\n\nintGeneric sum:\n" + intSum.ToString() +

"\n\nstrintGeneric sum:\n" + strSum.ToString());

Таким образом, очевидно, что создан класс который можно использовать с параметрами различных типов, которые устанавливаются в момент объявления класса. Аналогичным образом можно объявить структуру или интерфейс:

public struct GenericStruct<TYPE>

{

public TYPE someField;

}

public interface IGeneric<TYPE>

{

TYPE SomeMethod();

TYPE AnotherMethod();

}

Более того, параметризованные типы могут быть использованы при объявлении делегатов функций. Продемонстрирую эту возможность используя объявленный выше класс Generics.

// Объявляем делегат

public delegate DELEGATETYPE GenericDelegate<DELEGATETYPE, PARAMTYPE> (PARAMTYPE Param);

// используем делегат

Generics<string, string> strGeneric = new Generics<string, string>("Hello", "world");

GenericDelegate<string, string> genDelegate =

new GenericDelegate<string, string>(strGeneric.ToStringFunction);

// вызов делагата

MessageBox.Show(genDelegate(" my "));