Подтвердить что ты не робот

Разница между общим аргументом, ограниченным интерфейсом, и просто использованием интерфейса

В чем разница между этим:

void MyMethod(IMyInterface value)
{
    //...
}

и это:

void MyMethod<T>(T value) where T : IMyInterface
{
    //...
}
4b9b3361

Ответ 1

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

В нестандартном случае вы не можете гарантировать доступ к базовому типу объекта. Большинство типов вы можете захватить value.GetType(), но пользователь может пройти Null и помешать вам.

Ответ 2

Джаред упомянул некоторые моменты; еще один интересный: с помощью дженериков вы можете избежать бокса типов значений, пока вы в основном не прикасаетесь к нему... поэтому я мог бы иметь struct Foo : IMyInterface и передать его, и он не получит коробку.

Разница становится более заметной с такими вещами, как коллекции:

static void Foo(IEnumerable<IMyInterface> data) {}

против

static void Foo<T>(IEnumerable<T> data) 
    where T : IMyInterface {}

Теперь, поскольку С# 3.0 не имеет ковариации (кроме массивов), я не могу передать List<Bar> в верхнюю, даже если Bar : IMyInterface - но я могу со вторым (неявным T = Bar).

Ответ 3

Общая версия потребует .NET 2.0.

Но если серьезно, хотя они выглядят одинаково, между ними существуют принципиальные различия. Одно из отличий заключается в том, что во время выполнения JIT-компилятор будет генерировать код для каждого типа значений, который будет использоваться для общей версии. В нестандартной версии для ввода значений в класс будут использоваться типы значений.

Разница также будет иметь значение при работе с делегатами. Подпись MyMethod<int> соответствует void MyDelegate(int x), а не общий вариант не соответствует.

Ответ 4

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

class Dictionary<TKey,TVal>
    where TKey: IComparable, IEnumerable
    where TVal: IValue
{ ... }

Ответ 5

Еще одно небольшое отличие заключается в том, что вы не можете перегружать метод только ограничениями (ограничения не являются частью сигнатуры метода):

Это незаконно:

void MyMethod<T>(T value) where T : IMyInterface
{
    //...
}

void MyMethod<T>(T value) where T : IMyInterface2
{
    //...
}

пока это законно:

void MyMethod(IMyInterface value)
{
    //...
}

void MyMethod(IMyInterface2 value)
{
    //...
}

Ответ 6

Еще одно предостережение, которое следует учитывать при этом сценарии, - это тот факт, что использование "где T: <% ваш базовый интерфейс или абстракция% > " может быть переоценено в дженериках, что делает ваш родовой тип не общим для себя.

IE: Помните, что, изолировав ваш общий метод на IMyInterface, вы изолируете этот метод только теми типами, которые реализуют IMyInterface. Поэтому, если вы просто решили использовать IMyInterface на основе хороших принципов ООП, но у вас есть только одно (или в некоторых случаях очень небольшое число) потенциального типа в любом месте, где будет реализовываться этот интерфейс, то вы победили цель использования дженериков. В этом случае первый вариант будет лучше.

Используйте только "where" в своем родовом типе, когда у вас будет более широкий набор типов, на самом деле реализующих IMyInterface.

Ответ 7

Еще одно отличие общих методов (хотя и не для вашего примера) состоит в том, что если у вас есть метод типа T MungeThing<T>(T it) where T:IMungeable<T> и class Fnord реализует IMungeable<Fnord>, тогда код сможет сказать: Fnord thing1, thing2; ... thing1 = MungeThing(thing2); и компилятор будет знать, что MungeThing вернет Fnord вместо некоторой произвольной реализации IMungable.