Если dDelegate является известным типом (т. Е. Action), вы всегда можете привести его к нему и вызвать его напрямую.
Сказав это, если вы находитесь на .NET3.5, вы можете использовать деревья выражений, чтобы получить честный результат.немного оптимизации.В моем примере используется параллельный словарь в .NET4, но его можно заменить обычным словарем и блокировкой.
Идея заключается в следующем: делегат хранит информацию о том, какой метод вызывается.Для каждого вызываемого уникального метода я создаю (используя деревья выражений) скомпилированный делегат, который вызывает этот конкретный метод.Создание скомпилированного делегата стоит дорого, поэтому важно его кешировать, но после создания скомпилированный делегат работает так же быстро, как обычный делегат.
На моей машине 3 000 000 вызовов заняли 1 с скомпилированным делегатом и 16 с с DynamicInvoke,
// Comment this line to use DynamicInvoke instead as a comparison
#define USE_FAST_INVOKE
namespace DynInvoke
{
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
static class Program
{
delegate void CachedMethodDelegate (object instance, object sender, EventArgs param);
readonly static ConcurrentDictionary<MethodInfo, CachedMethodDelegate> s_cachedMethods =
new ConcurrentDictionary<MethodInfo, CachedMethodDelegate> ();
public static void InvokeExternal(Delegate d, object sender, EventArgs param)
{
if (d != null)
{
//Check each invocation target
foreach (var dDelgate in d.GetInvocationList())
{
if (
dDelgate.Target != null
&& dDelgate.Target is System.ComponentModel.ISynchronizeInvoke
&& ((System.ComponentModel.ISynchronizeInvoke)(dDelgate.Target)).InvokeRequired
)
{
//If target is ISynchronizeInvoke and Invoke is required, invoke via ISynchronizeInvoke
((System.ComponentModel.ISynchronizeInvoke)(dDelgate.Target)).Invoke(dDelgate, new object[] { sender, param });
}
else
{
#if USE_FAST_INVOKE
var methodInfo = dDelgate.Method;
var del = s_cachedMethods.GetOrAdd (methodInfo, CreateDelegate);
del (dDelgate.Target, sender, param);
#else
dDelgate.DynamicInvoke (sender, param);
#endif
}
}
}
}
static CachedMethodDelegate CreateDelegate (MethodInfo methodInfo)
{
var instance = Expression.Parameter (typeof (object), "instance");
var sender = Expression.Parameter (typeof (object), "sender");
var parameter = Expression.Parameter (typeof (EventArgs), "parameter");
var lambda = Expression.Lambda<CachedMethodDelegate>(
Expression.Call (
Expression.Convert (instance, methodInfo.DeclaringType),
methodInfo,
sender,
parameter
),
instance,
sender,
parameter
);
return lambda.Compile ();
}
class MyEventListener
{
public int Count;
public void Receive (object sender, EventArgs param)
{
++Count;
}
}
class MyEventSource
{
public event Action<object, EventArgs> AnEvent;
public void InvokeAnEvent (EventArgs arg2)
{
InvokeExternal (AnEvent, this, arg2);
}
}
static void Main(string[] args)
{
var eventListener = new MyEventListener ();
var eventSource = new MyEventSource ();
eventSource.AnEvent += eventListener.Receive;
var eventArgs = new EventArgs ();
eventSource.InvokeAnEvent (eventArgs);
const int Count = 3000000;
var then = DateTime.Now;
for (var iter = 0; iter < Count; ++iter)
{
eventSource.InvokeAnEvent (eventArgs);
}
var diff = DateTime.Now - then;
Console.WriteLine (
"{0} calls took {1:0.00} seconds (listener received {2} calls)",
Count,
diff.TotalSeconds,
eventListener.Count
);
Console.ReadKey ();
}
}
}
Редактировать: Так как OP использует .NET2, я добавил пример, который должен быть совместим со средой выполнения .NET2 (поскольку я использую VS2010, я мог бы по ошибке использовать некоторые новые языковые функции, но я компилировал, используя среду выполнения .NET2).
// Comment this line to use DynamicInvoke instead as a comparison
#define USE_FASTER_INVOKE
namespace DynInvoke
{
using System;
using System.Globalization;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
static class FasterInvoke
{
delegate void CachedMethodDelegate (object instance, object sender, EventArgs param);
readonly static Dictionary<MethodInfo, CachedMethodDelegate> s_cachedMethods =
new Dictionary<MethodInfo, CachedMethodDelegate> ();
public static void InvokeExternal (Delegate d, object sender, EventArgs param)
{
if (d != null)
{
Delegate[] invocationList = d.GetInvocationList ();
foreach (Delegate subDelegate in invocationList)
{
object target = subDelegate.Target;
if (
target != null
&& target is ISynchronizeInvoke
&& ((ISynchronizeInvoke)target).InvokeRequired
)
{
((ISynchronizeInvoke)target).Invoke (subDelegate, new[] { sender, param });
}
else
{
#if USE_FASTER_INVOKE
MethodInfo methodInfo = subDelegate.Method;
CachedMethodDelegate cachedMethodDelegate;
bool result;
lock (s_cachedMethods)
{
result = s_cachedMethods.TryGetValue (methodInfo, out cachedMethodDelegate);
}
if (!result)
{
cachedMethodDelegate = CreateDelegate (methodInfo);
lock (s_cachedMethods)
{
s_cachedMethods[methodInfo] = cachedMethodDelegate;
}
}
cachedMethodDelegate (target, sender, param);
#else
subDelegate.DynamicInvoke (sender, param);
#endif
}
}
}
}
static CachedMethodDelegate CreateDelegate (MethodInfo methodInfo)
{
if (!methodInfo.DeclaringType.IsClass)
{
throw CreateArgumentExceptionForMethodInfo (
methodInfo,
"Declaring type must be class for method: {0}.{1}"
);
}
if (methodInfo.ReturnType != typeof (void))
{
throw CreateArgumentExceptionForMethodInfo (
methodInfo,
"Method must return void: {0}.{1}"
);
}
ParameterInfo[] parameters = methodInfo.GetParameters ();
if (parameters.Length != 2)
{
throw CreateArgumentExceptionForMethodInfo (
methodInfo,
"Method must have exactly two parameters: {0}.{1}"
);
}
if (parameters[0].ParameterType != typeof (object))
{
throw CreateArgumentExceptionForMethodInfo (
methodInfo,
"Method first parameter must be of type object: {0}.{1}"
);
}
Type secondParameterType = parameters[1].ParameterType;
if (!typeof (EventArgs).IsAssignableFrom (secondParameterType))
{
throw CreateArgumentExceptionForMethodInfo (
methodInfo,
"Method second parameter must assignable to a variable of type EventArgs: {0}.{1}"
);
}
// Below is equivalent to a method like this (if this was expressible in C#):
// void Invoke (object instance, object sender, EventArgs args)
// {
// ((<%=methodInfo.DeclaringType%>)instance).<%=methodInfo.Name%> (
// sender,
// (<%=secondParameterType%>)args
// );
// }
DynamicMethod dynamicMethod = new DynamicMethod (
String.Format (
CultureInfo.InvariantCulture,
"Run_{0}_{1}",
methodInfo.DeclaringType.Name,
methodInfo.Name
),
null,
new[]
{
typeof (object),
typeof (object),
typeof (EventArgs)
},
true
);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator ();
ilGenerator.Emit (OpCodes.Ldarg_0);
ilGenerator.Emit (OpCodes.Castclass, methodInfo.DeclaringType);
ilGenerator.Emit (OpCodes.Ldarg_1);
ilGenerator.Emit (OpCodes.Ldarg_2);
ilGenerator.Emit (OpCodes.Isinst, secondParameterType);
if (methodInfo.IsVirtual)
{
ilGenerator.EmitCall (OpCodes.Callvirt, methodInfo, null);
}
else
{
ilGenerator.EmitCall (OpCodes.Call, methodInfo, null);
}
ilGenerator.Emit (OpCodes.Ret);
return (CachedMethodDelegate)dynamicMethod.CreateDelegate (typeof (CachedMethodDelegate));
}
static Exception CreateArgumentExceptionForMethodInfo (
MethodInfo methodInfo,
string message
)
{
return new ArgumentException (
String.Format (
CultureInfo.InvariantCulture,
message,
methodInfo.DeclaringType.FullName,
methodInfo.Name
),
"methodInfo"
);
}
}
static class Program
{
class MyEventArgs : EventArgs
{
}
class MyEventListener
{
public int Count;
public void Receive (object sender, MyEventArgs param)
{
++Count;
}
}
delegate void MyEventHandler (object sender, MyEventArgs args);
class MyEventSource
{
public event MyEventHandler AnEvent;
public void InvokeAnEvent (MyEventArgs arg2)
{
FasterInvoke.InvokeExternal (AnEvent, this, arg2);
}
}
static void Main (string[] args)
{
MyEventListener eventListener = new MyEventListener ();
MyEventSource eventSource = new MyEventSource ();
eventSource.AnEvent += eventListener.Receive;
MyEventArgs eventArgs = new MyEventArgs ();
eventSource.InvokeAnEvent (eventArgs);
const int count = 5000000;
DateTime then = DateTime.Now;
for (int iter = 0; iter < count; ++iter)
{
eventSource.InvokeAnEvent (eventArgs);
}
TimeSpan diff = DateTime.Now - then;
Console.WriteLine (
"{0} calls took {1:0.00} seconds (listener received {2} calls)",
count,
diff.TotalSeconds,
eventListener.Count
);
Console.ReadKey ();
}
}
}