Theory and Tasks for Students - Spring 2019
Generic types (обобщённые типы)

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

Для таких случаев был создан синтаксис обобщённых типов, которым в качестве дополнительного аргумента к названию типа даётся один или несколько других. Например: class G<T> { public T[] array; public G(int n) { this.array = new T[n]; } } class OtherClass { void Method() { G<int> arr_int = new G<int>(10); // внутри будет массив int[10] G<string> arr_str = new G<string>(15); // внутри будет массив string[10] G<G<PointF>> arr_arr_PointF = new G<G<PointF>>(8); // массив из 8 массивов из точек с вещественными координатами for (int i = 0; i < arr_arr_PointF.array.Length; i++) { arr_arr_PointF.array[i] = new G<PointF>(i + 1); // подмассивы из 1..8 точек с вещественными координатами } } } Аргументов типа может быть несколько, как, например, у типов Func<..> или Tuple<..>. Важно понимать, что типы A<int>, A<float> и A являются разными!

На аргумент типа можно наложить ограничение, чтобы он наследовал какой-либо другой тип или реализовывал какие-либо интерфейсы: interface I1 {} struct A : I1 {} class B {} class C : I1 {} class G<T> where T : struct, I1 { public T field; } class OtherClass { void Method() { var obj1 = new G<A>(); //var obj2 = new G<B>(); // ошибка: класс B не может быть конвертирован в I1 //var obj2 = new G<C>(); // ошибка: класс C не является структурой } }

Аргумент типа можно использовать не только у всего класса, но и отдельных методов: class G { public T[] Reverse<T>(T[] arr) { var tmp = new T[arr.Length]; int i = arr.Length - 1; foreach (var item in arr) { tmp[i--] = item; } return tmp; } } class OtherClass { void Method() { var _g = new G(); var res = _g.Reverse<int>(new int[4] {1, 2, 3, 4}); // res: [4, 3, 2, 1] } } Переданный тип не обязательно использовать в списке аргументов или как тип результата.