Универсальные шаблоны или ограничения (на анг. generics) позволяют указывать тип, который конкретизируется при использовании.
Выше мы уже видели пример с generics в классе
List<T>
 в котором тип элементов списка определяется в момент его объявления

List<string> dinos = new List<string>();

dinos.AdD("Tyrannosaurus");

dinos.Add("Amargasaurus");

dinos.Add("Mamenchisaurus");

dinos[0];

dinos[1];

dinos[2];

Tyranosaurus

Amargasaurus

Mamenchisaurus

   
DINOS.Remove("Tyrannosaurus"); //=>
 
 
Remove_Tyranosaurus
Deinosuchus
DINOS[0]=Deinosuchus”; //=>
 

 

Класс на основе Object

Класс на основе Generics (шаблона или обобщения)

class Obj
{
    public Object t;
    public Object u;
    public Obj(Object t, Object u)
    {
        this.t = t;
        this.u = u;
    }
}

 

    Obj ob = new Obj("Hello", "World");

    Console.WriteLine((string)ob.t + (string)ob.u);
    Obj od = new Obj(30.3, 2015);

    Console.WriteLine((double)od.t +(int)od.u));

 

class Gen<T, U>
{
    public T t;
    public U u;
    public Gen(T t, U u)
    {
        this.t = t;
        this.u = u;
    }
}

 

Gen<string, string> ob =new Gen<string,string>("Hi", "Wrld");

Gen<double, int> od = new Gen<double, int>(30, 2014);

Console.WriteLine(ob.t + ob.u);
Console.WriteLine(Convert.ToString(od.t  + od.u));

 

 

Универсальные методы

 

В некоторых случаях достаточно параметризировать не весь пользовательский тип, а только отдельный метод. Универсальные методы (generic methods) объявляются с использованием параметров-типов в угловых скобках после имени метода. Как и при описании универсальных типов, универсальные методы могут содержать ограничения на параметр-тип.

 

void PushMultiple<T>(Stack<T> stack, params T[] values)

{

    foreach (T value in values)

    {

        stack.Push(value);

    }

}

 

Stack<int> stack = new Stack<int>();

PushMultiple<int>(stack, 1, 2, 3, 4);

 

var stack = new Stack<int>();

PushMultiple(stack, 1, 2, 3, 4);

  

Рассмотрим пример перезагрузки Generics метода:

public class C

{

    public static void M(string a, string b) { . . . }

    public static void M<T>(T a, T b) { . . . }

}

 

int a = 10;

byte b = 20;

string s = "ABC";

C.M(a, b);               // C.M<int>(a,b)

C.M(s, s);               // C.M(s,s)

C.M<string>(s, s);       // C.M<string>(s,s)

 

В первом случае компилятор сконструирует метод M<T>() как M<int>(), потому что к типу int приводится и переменная a, и переменная b. Второй вызов показывает, что при наличии альтернатив компилятор всегда выбирает версию метода без универсального параметра, если только метод не сконструирован явно.