Первая версия всегда будет медленнее. Проще говоря, с этой версией все, что может сделать компилятор во время компиляции, теперь должно быть выполнено во время выполнения, т.е. проверка типов a и b, определение, поддерживают ли они оператор + и так далее. Он будет кэшировать большую часть этого материала, но все же это гораздо больше работы, чем вызов делегата.
Во второй версии все эти проверки могут быть выполнены во время компиляции. Стоимость во время выполнения - только создание и вызов делегата.
Например, рассмотрим эти методы:
static T Add<T>(T a, T b)
{
return ((dynamic)a) + ((dynamic)b);
}
в отличие от
static T Add<T>(T a, T b, Func<T, T, T> adder)
{
return adder(a, b);
}
Это то, что компилятор генерирует из первого метода:
private static T Add<T>(T a, T b)
{
if (Program.<Add>o__SiteContainer0<T>.<>p__Site1 == null)
{
Program.<Add>o__SiteContainer0<T>.<>p__Site1 = CallSite<Func<CallSite, object, T>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(T), typeof(Program)));
}
Func<CallSite, object, T> arg_98_0 = Program.<Add>o__SiteContainer0<T>.<>p__Site1.Target;
CallSite arg_98_1 = Program.<Add>o__SiteContainer0<T>.<>p__Site1;
if (Program.<Add>o__SiteContainer0<T>.<>p__Site2 == null)
{
Program.<Add>o__SiteContainer0<T>.<>p__Site2 = CallSite<Func<CallSite, object, object, object>>.Create(Binder.BinaryOperation(CSharpBinderFlags.None, ExpressionType.Add, typeof(Program), new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
return arg_98_0(arg_98_1, Program.<Add>o__SiteContainer0<T>.<>p__Site2.Target(Program.<Add>o__SiteContainer0<T>.<>p__Site2, a, b));
}
Я провел несколько грубых измерений, и на моей машине первая версия примерно в 5 раз медленнее второй. Я должен признать, что ожидал, что разница будет больше.
Обновление: как доказать, почему это медленнее, чем делегат: учитывая, что сгенерированный код сайта вызова также включает вызов делегата (arg_98_0
), этот фрагмент кода (вызов делегата + X) должен обязательно будет медленнее, чем использование только делегата.