我正在创建一个框架,该框架包含围绕库(特别是SharpBrake)的包装,该包装通过反射与SharpBrake进行所有交互,因此对库的第3方不存在对库的严格依赖。

如果我框架的第3方想使用SharpBrake,他们可以将SharpBrake.dll塞进bin文件夹中,但是如果不这样做,他们可以忘了它。如果我的框架具有对SharpBrake类型的显式引用,则我的框架用户会在缺少SharpBrake.dll的运行时遇到异常,这是我所不希望的。

因此,我的包装器首先从磁盘加载SharpBrake.dll,找到AirbrakeClient类型,然后将指向AirbrakeClient.Send(AirbrakeNotice)方法的委托存储在私有字段中。但是,我的问题是,由于Send()方法采用了AirbrakeNotice对象,并且我不能直接引用AirbrakeNotice对象,因此我需要以某种方式将Send()方法转换为Action<object>

我强烈认为这是不可能的,但是我想在决定公开Delegate和使用DynamicInvoke()之前探索所有选项,我认为这远非最佳的性能。我想做的是以下几点:

Type clientType = exportedTypes.FirstOrDefault(type => type.Name == "AirbrakeClient");
Type noticeType = exportedTypes.FirstOrDefault(type => type.Name == "AirbrakeNotice");
MethodInfo sendMethod = clientType.GetMethod("Send", new[] { noticeType });
object client = Activator.CreateInstance(clientType);
Type actionType = Expression.GetActionType(noticeType);
Delegate sendMethodDelegate = Delegate.CreateDelegate(actionType, client, sendMethod);

// This fails with an InvalidCastException:
Action<object> sendAction = (Action<object>)sendMethodDelegate;


但是,此操作失败,但有以下异常:


  System.InvalidCastException:无法将类型为“ System.Action`1 [SharpBrake.Serialization.AirbrakeNotice]”的对象转换为类型为“ System.Action`1 [System.Object]”的对象。


显然,因为sendMethodDelegateAction<AirbrakeNotice>而不是Action<object>。由于我无法在代码中提及AirbrakeNotice,因此我被迫这样做:

Action<object> sendAction = x => sendMethodDelegate.DynamicInvoke(x);


或直接暴露Delegate sendMethodDelegate。这可能吗?我知道有可能进入object的类型不同于AirbrakeNotice的类型的情况,这很不好,但是无论如何,无论看到多少反射,我都希望弄乱,我希望某个地方存在漏洞。

最佳答案

如果不需要低于C#4的支持,则可以使用dynamicDynamicInvoke获得更高的性能。

Action<dynamic> sendAction = x => sendMethodDelegate(x);


实际上,我想如果可以使用动态,您甚至不需要以上内容,因为如果您这样做,它将提高性能并简化所有操作:

Type clientType = exportedTypes.FirstOrDefault(type => type.Name == "AirbrakeClient");
dynamic client = Activator.CreateInstance(clientType);

...
client.Send(anAirbrakeNotice);


但是,如果您需要支持.net 3.5 jon skeets,那么使用表达式树的答案肯定是解决之道。

10-07 23:42