Общие типы (Generics)
Общие (или параметризованные) типы (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 "));
Преимущества использования общих типов
"Вау!" - воскликнут программисты на C++ использующие в своей работе также и C#. "И что нам с того?" - скажут программисты на C# никогда не работавшие с шаблонами С++. Какие же преимущества дает использование общих типов?
Наиболее очевидное - повторное использование кода. Нет необходимости создавать два идентичных класса, отличающихся только типами параметров, достаточно создать один с параметризованными типами. При этом использование параметризованных типов позволяет создавать единый программный код для работы с различными типами данных. Например, единожды написанный алгоритм может работать и с целыми числами и с числами с плавающей десятичной точкой, при этом не производя на каждом шаге проверку/приведение типа. Так Generics вытесняют классы объявленные с использованием типа object.
Повышение производительности кода по сравнению с использование параметров типа object - нет необходимости выполнять приведение, как уже сказано выше, на каждом шаге, за счет чего получается выигрыш в производительности.
Проверка типов в момент компиляции программы. Поскольку не используются параметры типа object, то компилятор может выполнить проверку типа каждого параметра в момент компиляции, поскольку типы для Generic классов жестко задаются в момент объявления переменных классов этого типа.
К сожалению, опытных программистов на C++ я должен несколько расстроить. Общие типы все-таки не соответствуют шаблонам в C++, поскольку параметризованные типы в C# не могут иметь типов по умолчанию. Параметризованные типы не могут быть использованы в качестве базовых классов для общих типов. Также в C# не допускается использования Generic классов в качестве параметров типов в других Generic классах.
Но, несмотря на это, общие типы все-таки весьма полезным новшеством языка C#, особенно ценным и удобным для разработчиков использующих математические алгоритмы, поскольку преимущества использования Generic классов очевидны.