c# - 开放实例委托(delegate)上的协变实例类型

标签 c# .net reflection delegates

我正在尝试为共享公共(public)签名但在许多不同且不相关的类型上定义的方法创建开放实例委托(delegate)。这些方法用自定义属性标记,在运行时我查找所有用这个属性标记的方法,以便从它们的MethodInfo 中构造委托(delegate)。 s。例如,给定委托(delegate):

delegate void OpenActionDelegate(object instance, float someParam);

我想匹配的方法:
void Foo.SomeAction(float someParam);
void Bar.SomeOtherAction(float someParam);

在哪里 FooBar是完全不相关的类。配备MethodInfo对于任何一种方法,我都希望最终能够像这样获得一个开放的委托(delegate):
MethodInfo fm = typeof(Foo).GetMethod("SomeAction", BindingFlags.Public | BindingFlags.Instance);
MethodInfo bm = typeof(Bar).GetMethod("SomeOtherAction", BindingFlags.Public | BindingFlags.Instance);
OpenActionDelegate fd = (OpenActionDelegate)Delegate.CreateDelegate(typeof(OpenActionDelegate), fm);
OpenActionDelegate bd = (OpenActionDelegate)Delegate.CreateDelegate(typeof(OpenActionDelegate), bm);

我遇到的问题是委托(delegate)中显式实例规范的类型。由于这些方法没有保证它们将被定义的基本类型,我尝试设置 object .但试图绑定(bind) MethodInfo失败,大概是因为绑定(bind)委托(delegate)时参数类型不是协变的。切换委托(delegate)签名以使实例参数的类型为 FooBar用于绑定(bind)相应的MethodInfo .

我不相信像这样绑定(bind)开放委托(delegate)实际上是可能的,因为这样显式实例参数就不会是调用该方法的适当类型。困扰我的是可以将封闭的委托(delegate)绑定(bind)到 MethodInfo任何声明类型,因为它不包括麻烦的实例类型。例如,我可以将封闭的委托(delegate)绑定(bind)到 null实例,然后使用 GetField("_target").SetValue(del, instance)在调用它们之前在委托(delegate)上。但这有点骇人听闻。

现在,如果有替代解决方案,我希望这样做的原因是在直接调用 MethodInfo 时避免堆分配和值类型装箱。 s,即:
someActionInfo.Invoke(instance, new object[] { someParam });

这会导致 float 的装箱。类型和 object[]数组在堆上分配,两者都会缓慢地为其他一次性调用生成堆垃圾。

最佳答案

参数类型,包括隐含的“this”参数,显然不能是协变的并且仍然是类型安全的。暂时忘记实例方法,只考虑静态方法。如果你有

static void Foo(Mammal m) {}

那么你不能将它分配给一个接受动物的委托(delegate),因为该委托(delegate)的调用者可以传入一个水母。不过,您可以将其分配给带长颈鹿的委托(delegate),因为调用者只能传入长颈鹿,而长颈鹿是哺乳动物。

简而言之,为了类型安全,您需要逆变,而不是参数的协变。

C# 确实以多种方式支持这一点。首先,在 C# 4 中,您可以这样做:
Action<Mammal> aa = m=>m.GrowHair();
Action<Giraffe> ag = aa;

也就是说,当可变类型参数是引用类型时,通用操作类型的转换是逆变的。

其次,在 C# 2 及更高版本中,您可以这样做:
Action<Giraffe> aa = myMammal.GrowHair;

也就是说,方法组到委托(delegate)的转换在方法的参数类型中是逆变的。

但是您想要的协方差类型不是类型安全的,因此不受支持。

关于c# - 开放实例委托(delegate)上的协变实例类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6022891/

相关文章:

c# - LambdaExpression.Compile 和 Delegate.CreateDelegate 之间的区别

c# - 快速创建数千个线程并同时执行它们

c# - 反射(reflection) |发射|动态资源生成 - 使用 IResourceWriter.AddResource(key,value) 添加的资源值无法读取

c# - 如何使用 C# 从文件中获取 EXIF 数据

c# - Entity Framework 中循环引用的 WCF Datacontract 序列化问题

c# - 获取 'DateTime.ToString()' 以输出与 'DateTime' 的 XML 序列化相同的字符串

c# ->1 客户端无法连接到 C# 中的服务器

java - 从java类中提取所有字段

java - java/scala 中的原始对象大小和 'as<SomePrimitive>Buffer() 方法调用

.NET 3.5 中的 C# 不序列化 Java Web 服务公开的 ENUM