Руководство C# Generics
View more Tutorials:

Пример ниже дает определение классу generics. KeyValue это класс generics, который содержит пару ключей и значение (key/value).
KeyValue.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class KeyValue<K, V> { private K key; private V value; public KeyValue(K key, V value) { this.key = key; this.value = value; } public K GetKey() { return key; } public void SetKey(K key) { this.key = key; } public V GetValue() { return value; } public void SetValue(V value) { this.value = value; } } }
K, V в классе KeyValue<K,V> называются параметрами generics, это определенный вид данных. При использовании этого класса, вам нужно опредеилть конкретный параметр.
Посмотрим пример использования класса KeyValue.
KeyValueDemo.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class KeyValueDemo { public static void Main(string[] args) { // Создать объект KeyValue. // int: Номер телефона (K = int) // string: Имя пользователя. (V = string). KeyValue<int, string> entry = new KeyValue<int, string>(12000111, "Tom"); // C# понимает возвращенный вид как int (K = int). int phone = entry.GetKey(); // C# понимает возвращенный вид как string (V = string). string name = entry.GetValue(); Console.WriteLine("Phone = " + phone + " / name = " + name); Console.Read(); } } }
Запуск примера:

Расширенный класс из класса generics, который может определить вид параметра для generics, оставить параметры generics или добавить параметры generics.
Пример 1:
PhoneNameEntry.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { // Данный класс расширен из класса KeyValue<K,V> // И ясно определяет вид для 2 параметров K & V // K = int (Номер телефона). // V = string (Имя пользователя). public class PhoneNameEntry : KeyValue<int, string> { public PhoneNameEntry(int key, string value) : base(key, value) { } } }
Пример использования PhoneNameEntry:
PhoneNameEntryDemo.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class PhoneNameEntryDemo { public static void Main(string[] args) { PhoneNameEntry entry = new PhoneNameEntry(12000111, "Tom"); // C# понимает возвращенный вид как int. int phone = entry.GetKey(); // C# понимает возвращенный вид как string. string name = entry.GetValue(); Console.WriteLine("Phone = " + phone + " / name = " + name); Console.Read(); } } }
Example 2:
StringAndValueEntry.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { // Данный класс расширен из класса KeyValue<K,V> // Ясно определить параметр K как String. // Оставить вид параметра generic V. public class StringAndValueEntry<V> : KeyValue<string, V> { public StringAndValueEntry(string key, V value) : base(key, value) { } } }
Пример использования класса StringAndValueEntry:
StringAndValueEntryDemo.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class StringAndValueEntryDemo { public static void main(String[] args) { // (Код сотрудника, Имя сотрудника). // V = string (Имя сотрудника) StringAndValueEntry<String> entry = new StringAndValueEntry<String>("E001", "Tom"); String empNumber = entry.GetKey(); String empName = entry.GetValue(); Console.WriteLine("Emp Number = " + empNumber); Console.WriteLine("Emp Name = " + empName); Console.Read(); } } }
Пример 3:
KeyValueInfo.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { // Данный класс расширен из класса KeyValue<K,V>. // Он имеет еще один параметр Generics I. public class KeyValueInfo<K, V, I> : KeyValue<K, V> { private I info; public KeyValueInfo(K key, V value) : base(key, value) { } public KeyValueInfo(K key, V value, I info) : base(key, value) { this.info = info; } public I GetInfo() { return info; } public void GetInfo(I info) { this.info = info; } } }
Interface с параметром Generics:
GenericInterface.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public interface GenericInterface<G> { G DoSomething(); } }
Например, класс выполняет (implements) Interface:
GenericInterfaceImpl.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class GenericInterfaceImpl<G> : GenericInterface<G> { private G something; public G DoSomething() { return something; } } }
Вы можете определить Exception с параметрами Generics.
MyException.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { class MyException<E> : ApplicationException { } }
Использование действительного Generic Exception:
UsingGenericExceptionValid01.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { class UsingGenericExceptionValid01 { public void SomeMethod() { try { // ... } // Действительно catch (MyException<string> e) { // Сделать что-то здесь. } // Действительно catch (MyException<int> e) { // Сделать что-то здесь. } catch (Exception e) { } } } }
Использование действительного Generic Exception:
UsingGenericExceptionValid02.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { class UsingGenericExceptionValid02<K> { public void SomeMethod() { try { // ... } // Действительно catch (MyException<string> e) { // Сделать что-то здесь. } // Действительно catch (MyException<K> e) { // Сделать что-то здесь. } catch (Exception e) { } } } }
Использование недействительного Generic Exception:
UsingGenericExceptionInvalid.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { class UsingGenericExceptionInvalid { public void SomeMethod() { try { // ... } // Действительно // catch (MyException<string> e) { // Сделать что-то здесь. } // Invalid (Unknown parameter K) *********** // catch (MyException<K> e) // { // ... // } catch (Exception e) { } } } }
Метод в класс или интерфейсе может быть стать Generic (generify).
MyUtils.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class MyUtils { // <K,V> : Говорит этот метод имеет 2 вида параметра K,V // Метод возвращает вид K. public static K GetKey<K, V>(KeyValue<K, V> entry) { K key = entry.GetKey(); return key; } // <K,V> : Говорит этот метод имеет 2 вида параметра K,V // Метод возвращает вид V. public static V GetValue<K, V>(KeyValue<K, V> entry) { V value = entry.GetValue(); return value; } // List<E>: Список содержит элементы вида E // Данный метод возвращает вид E. public static E GetFirstElement<E>(List<E> list, E defaultValue) { if (list == null || list.Count == 0) { return defaultValue; } E first = list.ElementAt(0); return first; } } }
Пример использования метода generics:
MyUtilsDemo.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { public class MyUtilsDemo { public static void Main(string[] args) { // K = int: Phone // V = string: Name KeyValue<int, string> entry1 = new KeyValue<int, String>(12000111, "Tom"); KeyValue<int, string> entry2 = new KeyValue<int, String>(12000112, "Jerry"); // (K = int). int phone = MyUtils.GetKey(entry1); Console.WriteLine("Phone = " + phone); // Списко содержит элементы вида KeyValue<int,string>. List<KeyValue<int, string>> list = new List<KeyValue<int, string>>(); // Добавить элемент в список. list.Add(entry1); list.Add(entry2); KeyValue<int, string> firstEntry = MyUtils.GetFirstElement(list, null); if (firstEntry != null) { Console.WriteLine("Value = " + firstEntry.GetValue()); } Console.Read(); } } }
Запуск примера:

Иногда вы хотите инициализировать объект Generic:
public void DoSomething<T>() { // Инициализировать объект Generic T t = new T(); // Error }
Причина ошибки выше, является то, что параметр Т возможно не имеет конструктор (constructor) T(), поэтому ам нужно добавить принуждение when T : new(). Посмотрим пример ниже:
GenericInitializationExample.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { class GenericInitializationExample { // Вид T должен быть видом, имеющим Constructor по умолчанию. public void DoSomeThing<T>() where T : new() { T t = new T(); } // Вид T должен быть видом, имеющим Constructor по умолчанию. // и расширенным из класса KeyValue. public void ToDoSomeThing<K>() where K: KeyValue<K,string>, new( ) { K key = new K(); } public T DoDefault<T>() { // Возвращает null если T является ссылочным видом (reference type). // Или 0 если T является видом числа (int, float,..) return default(T); } } }
В C# вы можете объявить массив Generics:
// Инициализировать массив. T[] myArray = new T[10];
GenericArrayExample.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GenericsTutorial { class GenericArrayExample { public static T[] FilledArray<T>(T value, int count) { T[] ret = new T[count]; for (int i = 0; i < count; i++) { ret[i] = value; } return ret; } public static void Main(string[] args) { string value = "Hello"; string[] filledArray = FilledArray<string>(value, 10); foreach (string s in filledArray) { Console.WriteLine(s); } } } }