我正在创建一个框架,该框架包含围绕库(特别是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]”的对象。
显然,因为
sendMethodDelegate
是Action<AirbrakeNotice>
而不是Action<object>
。由于我无法在代码中提及AirbrakeNotice
,因此我被迫这样做:Action<object> sendAction = x => sendMethodDelegate.DynamicInvoke(x);
或直接暴露
Delegate sendMethodDelegate
。这可能吗?我知道有可能进入object
的类型不同于AirbrakeNotice
的类型的情况,这很不好,但是无论如何,无论看到多少反射,我都希望弄乱,我希望某个地方存在漏洞。 最佳答案
如果不需要低于C#4的支持,则可以使用dynamic
与DynamicInvoke
获得更高的性能。
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,那么使用表达式树的答案肯定是解决之道。