Рассмотрим следующий C# код:С # дженерики производительности против интерфейса
interface IFace
{
void Do();
}
class Foo: IFace
{
public void Do() { /* some action */ }
}
class Bar
{
public void A(Foo foo)
{
foo.Do();
}
public void B<T>(T foo)
where T: IFace
{
foo.Do();
}
public void C(IFace foo)
{
foo.Do();
}
public void D<T>(T foo)
where T: class, IFace
{
foo.Do();
}
}
со следующим использованием:
Foo foo = new Foo();
Bar bar = new Bar();
MeasureExecutionTime(() => bar.A(foo), "A");
MeasureExecutionTime(() => bar.B(foo), "B");
MeasureExecutionTime(() => bar.C(foo), "C");
MeasureExecutionTime(() => bar.D(foo), "D");
Результаты (VS2015, .NET 4.5.2) являются:
A: 3,00 ns/op, 333,4 m/c
B: 5,74 ns/op, 174,3 mop/s
C: 5,55 нс/оп, 180,3 швабра/s
D: 5,64 нс/оп, 177,4 швабра/s
Я хочу знать, почему использование общий метод B
не имеет никакого преимущества перед использованием интерфейса в режимах x86 и x64 (например, шаблоны C++ и виртуальные вызовы). Общий метод даже немного медленнее, чем не общий метод на основе интерфейса (этот эффект является устойчивым и остается при замене измерений B и C).
Приложение: Код MeasureExecutionTime можно найти здесь: https://gist.github.com/anonymous/9d60f5d09868ed3a00ec00f413f6afb0
Обновление: Я проверил код на Mono, результаты следующие:
андрей @ Ubuntu-NAS:/данные/моно/JSON/64 $ моно Test.exe
А: 3.40 нс/оп, 294,0 швабры/с
В: 3.40 нс/оп, 293,7 швабры/с
С: 6.80 нс/оп, 147,1 шваброй/с
Д: 3.40 нс/оп, 294,2 швабры/с
Сгенерированный IL-код может быть найден здесь: https://gist.github.com/anonymous/58df84eda906e83c64ce1b4fdc5497fb
MS и Mono генерируют один и тот же IL-код, за исключением метода D
. Тем не менее, он не может объяснить разницу для метода B
. Если я буду запускать MS-сгенерированный код Mono без перекомпиляции, результаты для метода D
будут такими же, как для B
.
Почему * должен * быть какая-то разница? Общая реализация имеет те же условия, что и ваш интерфейсный подход, а именно, что оба экземпляра, предоставленные в качестве аргумента *, имеют * тип 'IFace'. Что означает 'где T: IFace' дает вам то, что' (IFace foo) 'doesn't? – HimBromBeere
Дженерики не похожи на шаблоны C++; Они не компилируются в их используемые формы во время компиляции. –
Потому что я ожидаю, что 'T' будет конкретным классом' Foo' и 'IFace', чтобы быть контрактом. Почему CLR, который знает тип во время выполнения, не генерирует эффективный код? –