【发布时间】:2011-09-30 11:21:22
【问题描述】:
我正在创建一个框架,其中包含一个库(特别是 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 不同类型的情况,这会很糟糕,但是看到无论如何你可以搞砸反射,我希望某处存在漏洞。
【问题讨论】:
-
如果您担心性能,我会避免扩展使用反射。如果您可以为您正在使用的课程提供
interface,那么我会创建一个。然后编写一个包装器,通过调用 SharpBreak 代码来实现接口,并将其填充到单独的 DLL 中。然后动态加载您的包装程序集和具体包装类型,并调用该接口。那么您就不必在方法级别进行反射了。 -
@Merlyn 我希望我能多次为你投票。
-
在快速查看他们的代码之后,这可能就像包装
SharpBrake.Extensions一样简单,它有两种方法。您是否需要在应用中调用更多的扩展方法? -
@MerlynMorgan-Graham,请发布带有代码示例的答案。我不太明白如何使用接口来实现我想要的松散耦合。
-
@asbjornu:您仍然必须使用反射,但仅限于类型。我添加了一个带有代码示例的答案。很难从该库中准确说出您在 your 应用程序中需要什么。该库的文档只是说要调用单个扩展方法,而您似乎正在尝试包装所有库。
标签: c# generics reflection delegates