Самые важные вещи о С# generics... извлеченные уроки

Что самое важное, что вы знаете о дженериках: скрытые функции, распространенные ошибки, лучшие и полезные методы, советы...

Я начинаю реализовывать большую часть своей библиотеки /API с помощью дженериков и собираюсь собирать наиболее распространенные шаблоны, советы и т.д., которые можно найти на практике.

Позвольте мне оформить вопрос: что самое главное, что вы узнали о дженериках?

Попробуйте привести примеры - было бы проще понять, в отличие от запутанных и чрезмерно сухих описаний

Спасибо

Этот вопрос несколько похож на вопрос Jon, хотя на другой предмет.

Ответ 1

Одна из самых важных вещей, которые я узнал, заключается в том, что вы можете ограничить параметры типового типа. Это может быть очень мощным, позволяя вам настроить класс только для определенных типов объектов и позволить вам использовать членов этого типа в вашем общем классе. Я понимаю, что это довольно фундаментально, но это одна из вещей, которая делает дженерики невероятно полезными.

Ответ 2

Каждая специализация родового типа рассматривается как уникальный тип, когда речь идет о таких вещах, как статические члены. Например, с этим типом:

class GenericType<T>
{
    public static int SomeValue;
}

Утверждение выполняется, если мы это сделаем:

GenericType<int>.SomeValue = 3;
Debug.Assert(GenericType<double>.SomeValue == 0);

Это происходит потому, что:

typeof(GenericType<int>) != typeof(GenericType<double>)

Даже если

typeof(GenericType<int>.GetGenericTypeDefinition() == typeof(GenericType<double>).GetGenericTypeDefinition()

Ответ 3

Понимать возможности и ограничения вывода типового типа в С#. Глубокое понимание того, что компилятор может и не может сделать, исходя из (например) типов параметров в вашем методе, можно использовать, чтобы сделать обычные случаи использования вашего API значительно более удобочитаемыми.

Ответ 4

Самый важный урок о дженериках, которые я узнал: чем больше вы их используете, тем лучше.

Ответ 5

Нет ковариации или contra-variance (по крайней мере, в 3.5). Помните об этом при разработке иерархии классов, которые включают параметры типового типа.

Ответ 6

Два интересных урока. Первый; со списками; попытайтесь думать в терминах T; подробнее см. здесь, но вкратце вам нужно использовать:

public void Foo<T>(IList<T> data) where T : SomeBaseClassOrInterface {}

и не:

public void Foo(IList<SomeBaseClassOrInterface> data) {}

Второе: следить за краевыми случаями;-p

Вы можете увидеть ловушку здесь?

static void Foo<T>() where T : new()
{
    T t = new T();
    Console.WriteLine(t.ToString()); // works fine
    Console.WriteLine(t.GetHashCode()); // works fine
    Console.WriteLine(t.Equals(t)); // works fine

    // so it looks like an object and smells like an object...

    // but this throws a NullReferenceException...
    Console.WriteLine(t.GetType()); // BOOM!!!
}

Ответ 7

Не знаю, важны ли они, но я узнал следующее:

Дженерики будут доступны только через отражение, если вы не знаете тип фриккина. В некоторых случаях вам могут понадобиться не общие интерфейсы для использования ваших общих классов в ситуациях, когда тип неизвестен.

Я чуть не сорвал голову, пока не почувствовал, что

public class Foo<T> where T : Foo<T> {
  public T CloneMe() ...
}

является вполне допустимым кодом и позволяет вашему базовому классу выставлять методы и свойства, относящиеся к специализированному классу..., который оказался в определении конечного автомата по его состояниям:

public abstract class Machine<S,M> where S : State<S,M> where M : Machine<S,M>  {
    protected S state;
}

public abstract class State<S,M> where S : State<S,M> where M : Machine<S,M> {
    protected M machine;
}

Дженерики могут стать немного громоздкими. На днях у меня было это:

List<Tuple<Expression<Func<DataTable,object>>,Expression<Func<DataTable,object>>>>

уф...

Ответ 8

MyGeneric<T> where T : IComparable

не делает

MyGeneric<IComparable> 

базовый класс.

Ответ 9

Я узнал, что дженерики являются мощным инструментом indeep, но неправильно используются, что приводит к очень нечитаемому коду.

Ответ 10

Прежде всего, важно знать, как работают Generics в С#. Эта статья дает вам хороший обзор дженериков Андерса Хейлсберга (отцом С#). Я не думаю, что использовать их как можно чаще - это хорошо. Используйте дженерики, когда они действительно имеют смысл. Всегда помните KISS и YAGNI (Keep It Simple Stupid, вам это не понадобится) от экстремального программирования.

Ответ 11

Общие типы делегатов всегда являются инвариантами типа.

Я столкнулся с проблемой, подобной той, что описана в ссылке ниже дня, и это вызвало некоторую путаницу, потому что я не понимал, почему мне пришлось снимать свою коллекцию.

http://www.theserverside.net/blogs/thread.tss?thread_id=47323