打开通用接口方法的委托

我正在尝试为通用接口方法创建一个打开的实例委托 ,但我一直收到NotSupportedException。 以下是无法运行的简化代码:

interface IFoo { void Bar(T j); } class Foo : IFoo { public void Bar(T j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action), null, bar); } 

最后一行抛出NotSupportedException,“不支持指定的方法”。 相比之下,非通用的开放实例委托运行良好:

 interface IFoo { void Bar(int j); } class Foo : IFoo { public void Bar(int j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar"); var x = Delegate.CreateDelegate(typeof(Action), null, bar); } 

封闭的通用代表也有效:

 interface IFoo { void Bar(T j); } class Foo : IFoo { public void Bar(T j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action), new Foo(), bar); } 

因此,封闭的通用委托和开放实例委托的配方分开工作,但组合时则不然。 它开始看起来像运行时错误或故意遗漏。 有人有任何见解吗?

这是对于那些发现这个问题的人的主题和特定问题的回顾(因为看起来OP已经在Microsoft Connect上获得了答案)。


回答

为通用接口方法创建开放实例通用委托是不可能的(正如Microsoft 在此处所证实的那样)。 目前,可以实现以下任何开放实例/闭合静态,通用/非generics,接口/类方法的组合(在答案的最后提供代码示例):

  • 用于非generics接口方法的open instance非generics委托
  • 用于通用接口方法的闭合静态generics委托
  • 用于非generics接口方法的闭合静态非generics委托
  • generics实例generics委托,用于generics类方法
  • 用于非generics类方法的open instance非generics委托
  • 用于generics类方法的闭合静态generics委托
  • 用于非generics类方法的闭合静态非generics委托

通常,通用接口方法的开放实例generics委托的最佳替代是generics方法的开放实例generics委托。


代码示例

  • 用于非generics接口方法的open instance非generics委托

     interface IFoo { void Bar(int j); } class Foo : IFoo { public void Bar(int j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar"); var x = Delegate.CreateDelegate(typeof(Action), null, bar); } 
  • 用于通用接口方法的闭合静态generics委托

      interface IFoo { void Bar(T j); } class Foo : IFoo { public void Bar(T j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action), new Foo(), bar); } 
  • 用于非generics接口方法的闭合静态非generics委托

      interface IFoo { void Bar(int j); } class Foo : IFoo { public void Bar(int j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar"); var x = Delegate.CreateDelegate(typeof(Action), new Foo(), bar); } 
  • generics实例generics委托,用于generics类方法

     class Foo { public void Bar(T j) { } } static void Main(string[] args) { var bar = typeof(Foo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action), null, bar); } 
  • 用于非generics类方法的open instance非generics委托

     class Foo { public void Bar(int j) { } } static void Main(string[] args) { var bar = typeof(Foo).GetMethod("Bar"); var x = Delegate.CreateDelegate(typeof(Action), null, bar); } 
  • 用于generics类方法的闭合静态generics委托

     class Foo { public void Bar(T j) { } } static void Main(string[] args) { var bar = typeof(Foo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action), new Foo(), bar); } 
  • 用于非generics类方法的闭合静态非generics委托

     class Foo { public void Bar(int j) { } } static void Main(string[] args) { var bar = typeof(Foo).GetMethod("Bar"); var x = Delegate.CreateDelegate(typeof(Action), new Foo(), bar); } 

微软已经回答说 ,CLR无法做到这一点已知问题,但在当前版本的.NET中无法解决。 现在仍然不清楚为什么这不可能,因为我在那里解释。 由于某种原因,开放代表不得重复使用CLR中其他地方使用的调度逻辑,这对我来说似乎很奇怪。

不同寻常的是,如果你真的需要这个并且不介意在这个问题上投入过多的基础设施,你可以使用ldvirtftncalli

这对我来说似乎很奇怪,因为我认为这是一个代表在幕后做的事情基本上做了以下……

 public class MyAction{ public virtual void Invoke(SomeClass @this) { ldarg.1 dup ldvirtftn SomeClass.GenericMethod calli void *(argument) ret } 

Ldvirtftn查找了要为此特定方法调用的函数指针。 如果使用非虚拟generics方法,则性能与绑定到同一函数的委托大致相同。 如果它是一个虚拟通用方法,它的速度大约是它的两倍,那说它仍然有效,所以这是一个很大的改进。
我使用reflection.emit创建了它,它似乎工作正常,它可以调用一个封闭的虚拟generics方法。 不幸的是,与委托不同,此类型绑定到特定方法。 但是,对接中的一个痛苦是运行时不允许您创建使用ldvirtftnldftncalli操作码的动态方法。

  public class SomeType { public virtual void DoNothing() { Console.WriteLine(typeof(T)); } } public abstract class MyAction { public abstract void Invoke(SomeType type); } public static void Main(string[] args) { TypeBuilder builder = AppDomain.CurrentDomain .DefineDynamicAssembly(new AssemblyName(MethodBase.GetCurrentMethod().DeclaringType.Name), AssemblyBuilderAccess.RunAndCollect) .DefineDynamicModule("Module").DefineType("MyType", TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, typeof (MyAction)); var ilgen = builder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual, CallingConventions.HasThis, typeof (void), new[] {typeof (SomeType)}).GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_1); ilgen.Emit(OpCodes.Dup); ilgen.Emit(OpCodes.Ldvirtftn, typeof (SomeType).GetMethod("DoNothing").MakeGenericMethod(typeof (int))); ilgen.Emit(OpCodes.Calli, SignatureHelper.GetMethodSigHelper(CallingConventions.HasThis, typeof (void))); ilgen.Emit(OpCodes.Ret); MyAction action = Activator.CreateInstance(builder.CreateType()) as MyAction; action.Invoke(new SomeType()); } 

如果您可以使用代码生成,则可以使用表达式树或dynamicmethod来调用该方法。 它比直接代表慢一点,但我们谈的是一个很小的开销。