Я провел несколько тестов (в .Net 3.5 ... позже я проверю дома, используя .Net 4).
Дело в том:
Получение объекта в качестве интерфейса и последующее выполнение метода происходит быстрее, чем получение делегата от метода с последующим вызовом делегата.
Учитывая, что переменная уже имеет правильный тип (интерфейс или делегат), и простой вызов ее делает делегата победителем.
По некоторым причинам получение делегата через интерфейсный метод (возможно, через любой виртуальный метод) НАМНОГО медленнее.
И, учитывая, что есть случаи, когда мы просто не можем предварительно сохранить делегат (как, например, в Dispatches), это может оправдать, почему интерфейсы работают быстрее.
Вот результаты:
Чтобы получить реальные результаты, скомпилируйте их в режиме выпуска и запустите за пределами Visual Studio.
Проверка прямых звонков дважды
00: 00: 00,5834988
00: 00: 00,5997071
Проверка вызовов интерфейса, получение интерфейса при каждом вызове
00: 00: 05,8998212
Проверка вызовов интерфейса, получение интерфейса один раз
00: 00: 05,3163224
Проверка действий (делегат) вызовов, получение действия при каждом вызове
00: 00: 17,1807980
Проверка вызовов Action (делегатов), получение Action один раз
00: 00: 05,3163224
Проверка действия (делегата) через интерфейсный метод, получая оба в
каждый звонок
00: 03: 50,7326056
Проверка действия (делегата) через метод интерфейса, получение
интерфейс один раз, делегат при каждом вызове
00: 03: 48,9141438
Проверка действия (делегата) через метод интерфейса, получение обоих одновременно
00: 00: 04,0036530
Как видите, прямые звонки действительно быстрые.
Сохранение интерфейса или делегата до этого, а затем только его вызов очень быстро.
Но получить делегата медленнее, чем получить интерфейс.
Необходимость получить делегата через интерфейсный метод (или виртуальный метод, не уверен) очень медленная (сравните 5 секунд получения объекта в качестве интерфейса с почти 4 минутами того же действия, чтобы получить действие).
Код, сгенерировавший эти результаты, находится здесь:
using System;
namespace ActionVersusInterface
{
public interface IRunnable
{
void Run();
}
public sealed class Runnable:
IRunnable
{
public void Run()
{
}
}
class Program
{
private const int COUNT = 1700000000;
static void Main(string[] args)
{
var r = new Runnable();
Console.WriteLine("To get real results, compile this in Release mode and");
Console.WriteLine("run it outside Visual Studio.");
Console.WriteLine();
Console.WriteLine("Checking direct calls twice");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
r.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
r.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking interface calls, getting the interface at every call");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
IRunnable interf = r;
interf.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking interface calls, getting the interface once");
{
DateTime begin = DateTime.Now;
IRunnable interf = r;
for (int i = 0; i < COUNT; i++)
{
interf.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) calls, getting the action at every call");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
Action a = r.Run;
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) calls, getting the Action once");
{
DateTime begin = DateTime.Now;
Action a = r.Run;
for (int i = 0; i < COUNT; i++)
{
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) over an interface method, getting both at every call");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
IRunnable interf = r;
Action a = interf.Run;
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) over an interface method, getting the interface once, the delegate at every call");
{
DateTime begin = DateTime.Now;
IRunnable interf = r;
for (int i = 0; i < COUNT; i++)
{
Action a = interf.Run;
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) over an interface method, getting both once");
{
DateTime begin = DateTime.Now;
IRunnable interf = r;
Action a = interf.Run;
for (int i = 0; i < COUNT; i++)
{
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.ReadLine();
}
}
}