简体   繁体   English

开放通用接口方法的委托

[英]Open delegate for generic interface method

I'm trying to create an open instance delegate for a generic interface method, but I keep receiving a NotSupportedException. 我正在尝试为通用接口方法创建一个打开的实例委托 ,但我一直收到NotSupportedException。 Here is the simplified code that won't run: 以下是无法运行的简化代码:

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

The last line throws NotSupportedException, "Specified method is not supported". 最后一行抛出NotSupportedException,“不支持指定的方法”。 By comparison, a non-generic open instance delegate runs fine: 相比之下,非通用的开放实例委托运行良好:

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<IFoo, int>), null, bar);
}

And a closed generic delegate also works: 封闭的通用委托也有效:

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

So the recipe for closed generic delegates and open instance delegates work separately, but not when combined. 因此,封闭的通用委托和开放实例委托的配方分开工作,但在组合时则不然。 It's starting to look like either a runtime bug, or intentional omission. 它开始看起来像运行时错误或故意遗漏。 Anyone have any insight here? 有人有任何见解吗?

This is a recap of the topic and this specific issue for those that find this question (since it seems the OP has already got his answer on Microsoft Connect). 这是对于那些发现这个问题的人的主题和特定问题的回顾(因为看起来OP已经在Microsoft Connect上获得了答案)。


Answer 回答

Creating an open instance generic delegate for a generic interface method is impossible (As confirmed by Microsoft here ). 为通用接口方法创建一个开放实例通用委托是不可能的(正如Microsoft 在此处所证实的那样)。 Currently, it is possible to implement any of the following combinations of open-instance/closed static, generic/non-generic, interface/class methods (with code samples provided at the end of the answer): 目前,可以实现以下任何开放实例/闭合静态,通用/非泛型,接口/类方法的组合(在答案的最后提供代码示例):

  • open instance non-generic delegate for a non-generic interface method 用于非泛型接口方法的open instance非泛型委托
  • closed static generic delegate for a generic interface method 用于通用接口方法的闭合静态泛型委托
  • closed static non-generic delegate for a non-generic interface method 用于非泛型接口方法的闭合静态非泛型委托
  • open instance generic delegate for a generic class method 泛型实例泛型委托,用于泛型类方法
  • open instance non-generic delegate for a non-generic class method 用于非泛型类方法的open实例非泛型委托
  • closed static generic delegate for a generic class method 用于泛型类方法的闭合静态泛型委托
  • closed static non-generic delegate for a non-generic class method 用于非泛型类方法的闭合静态非泛型委托

Usually, the best replacement for an open instance generic delegate for a generic interface method is an open instance generic delegate for a generic class method. 通常,通用接口方法的开放实例通用委托的最佳替代是泛型方法的开放实例泛型委托。


Code Samples 代码示例

  • open instance non-generic delegate for a non-generic interface method 用于非泛型接口方法的open instance非泛型委托

     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<IFoo, int>), null, bar); } 
  • closed static generic delegate for a generic interface method 用于通用接口方法的闭合静态泛型委托

      interface IFoo { void Bar<T>(T j); } class Foo : IFoo { public void Bar<T>(T j) { } } static void Main(string[] args) { var bar = typeof(IFoo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action<int>), new Foo(), bar); } 
  • closed static non-generic delegate for a non-generic interface method 用于非泛型接口方法的闭合静态非泛型委托

      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<int>), new Foo(), bar); } 
  • open instance generic delegate for a generic class method 泛型实例泛型委托,用于泛型类方法

     class Foo { public void Bar<T>(T j) { } } static void Main(string[] args) { var bar = typeof(Foo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action<Foo, int>), null, bar); } 
  • open instance non-generic delegate for a non-generic class method 用于非泛型类方法的open实例非泛型委托

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

     class Foo { public void Bar<T>(T j) { } } static void Main(string[] args) { var bar = typeof(Foo).GetMethod("Bar").MakeGenericMethod(typeof(int)); var x = Delegate.CreateDelegate(typeof(Action<int>), new Foo(), bar); } 
  • closed static non-generic delegate for a non-generic class method 用于非泛型类方法的闭合静态非泛型委托

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

Unusually if you really need this and don't mind throwing too much infrastructure at the problem, you can use ldvirtftn and calli . 不同寻常的是,如果你真的需要这个并且不介意在这个问题上投入过多的基础设施,你可以使用ldvirtftncalli

It seems very strange to me as that's what I thought was what a delegate did behind the scene basically do the following... 这对我来说似乎很奇怪,因为我认为这是代表在幕后所做的事基本上做了以下......

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

Ldvirtftn does a look up to figure out the function pointer to be invoked for this particular method. Ldvirtftn查找了要为此特定方法调用的函数指针。 If you use a non-virtual generic method the performance is about the same as a delegate bound to the same function. 如果使用非虚拟泛型方法,则性能与绑定到同一函数的委托大致相同。 And if it is a virtual generic method its about twice as slow, that said its still works so that's quite an improvement. 如果它是一个虚拟通用方法,它的速度大约是它的两倍,那说它仍然有效,所以这是一个很大的改进。
I created this using reflection.emit and it seems to work just fine and it can invoke a closed virtual generic method. 我使用reflection.emit创建它,它似乎工作得很好,它可以调用一个封闭的虚拟泛型方法。 Unfortunately, unlike a delegate this type is bound to a specific method. 不幸的是,与委托不同,此类型绑定到特定方法。 However, quite a pain in the butt is that the runtime doesn't allow you to create a dynamic method that uses ldvirtftn , ldftn , or calli opcode. 但是,对接的一个痛苦是运行时不允许您创建使用ldvirtftnldftncalli操作码的动态方法。

    public class SomeType
    {
        public virtual void DoNothing<T>()
        {
            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());
    }

If you are okay with code generation you can use expression trees or dynamicmethod to just invoke the method. 如果您对代码生成没问题,可以使用表达式树或dynamicmethod来调用该方法。 It's a little slower than a direct delegate but we're talking a tiny overhead. 它比直接代表慢一点,但我们谈的是一个很小的开销。

Microsoft has answered that it's a known problem that the CLR can't do this, but it can't be solved in the current version of .NET. 微软已经回答说 ,CLR无法做到这一点已知问题,但在当前版本的.NET中无法解决。 It's still not at all clear why this isn't possible as I explain there. 我仍然不清楚为什么这不可能,因为我在那里解释。 Open delegates must not reuse the dispatching logic used everywhere else in the CLR for some reason, which just seems bizarre to me. 由于某些原因,开放代表不得重复使用CLR中其他地方使用的调度逻辑,这对我来说似乎很奇怪。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM