Когда я должен или не должен использовать ограничения общего типа? - PullRequest
2 голосов
/ 02 августа 2009

У меня есть базовый класс:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

и два производных класса

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

Хорошо, теперь скажите, что у меня есть список предметов:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

и я хочу, чтобы все они вызывали свой метод DoSomething (). Я мог бы ожидать итерации списка и вызова их метода DoSomething (), поэтому предположим, что у меня есть метод для этого, который называется AllDoSomething (), который просто выполняет итерацию по списку и выполняет работу:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

В чем практическая разница следующего метода?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

Оба метода в реальном выражении, хотя и синтаксически разные, делают одно и то же.

Это просто разные способы сделать одно и то же? Я понимаю обобщения и ограничения типов, но не понимаю, почему в этом случае я бы использовал один способ вместо другого.

Ответы [ 3 ]

6 голосов
/ 02 августа 2009

Это потому, что на данный момент C # не поддерживает Ковариация .

Более формально, в C # v2.0, если T является подтип U, то T [] является подтипом U [], но G не является подтипом G (где G - любой общий тип). В терминология теории типов, мы опишем это поведение, сказав, что массив C # типы являются «ковариантными» и родовыми типы «инвариантны».

Ссылка: http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

Если у вас есть следующий метод:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

Где, если вы используете ограничение общего типа, оно будет.

Для получения дополнительной информации о Ковариантности и Контравариантности], прочитайте Серия сообщений Эрика Липперта .


Другие сообщения, которые стоит прочитать:

1 голос
/ 02 августа 2009

Предположим, у вас есть список:

List<Stuff1> l = // get from somewhere

Теперь попробуйте:

AllDoSomething(l);

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

Вы можете обойти это, сначала скопировав свой список Stuff1 в список StuffBase, чтобы сделать его совместимым с неуниверсальной версией. Но тогда предположим, что у вас есть метод:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

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

0 голосов
/ 02 августа 2009

В приведенном вами примере разницы нет, попробуйте следующее:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...