通用接口方法的开放委托
我正在尝试为通用接口方法创建 开放实例委托,但我保留收到 NotSupportedException。下面是不会运行的简化代码:
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);
}
最后一行抛出 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<IFoo, int>), null, bar);
}
并且封闭泛型委托也可以工作:
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);
}
因此封闭泛型委托和开放实例委托的配方可以单独工作,但组合时则不行。它开始看起来像是一个运行时错误,或者是故意遗漏。这里有人有任何见解吗?
I'm trying to create an open instance delegate for a generic interface method, but I keep receiving a 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". 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是针对那些找到此问题的人的主题和特定问题的回顾(因为 OP 似乎已经在 Microsoft Connect 上得到了答案)。
答案
为通用接口方法创建开放实例通用委托是不可能的(正如 Microsoft 此处)。
目前,可以实现以下任意开放实例/封闭静态、泛型/非泛型、接口/类方法的组合(答案末尾提供了代码示例)
通常,泛型接口方法的开放实例泛型委托的最佳替代方案是泛型类方法的开放实例泛型委托。
代码示例
为非泛型接口方法打开实例非泛型委托
泛型接口方法的封闭静态泛型委托
非泛型接口方法的封闭静态非泛型委托
泛型类方法的开放实例泛型委托
为非泛型类方法打开实例非泛型委托
泛型类方法的封闭静态泛型委托
非泛型类方法的封闭静态非泛型委托
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).
Answer
Creating an open instance generic delegate for a generic interface method is impossible (As confirmed by Microsoft here).
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):
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
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
closed static generic delegate for a generic class method
closed static non-generic delegate for a non-generic class method
不同寻常的是,如果您确实需要这个并且不介意在问题上投入太多基础设施,则可以使用 ldvirtftn 和 calli。
这对我来说似乎很奇怪,因为我认为委托在幕后所做的基本上是执行以下操作...
Ldvirtftn
进行查找以找出要为此特定调用的函数指针方法。如果您使用非虚拟泛型方法,则性能与绑定到同一函数的委托大致相同。如果它是一个虚拟通用方法,它的速度大约是原来的两倍,这表明它仍然有效,所以这是一个很大的改进。我使用 Reflection.emit 创建了这个,它似乎工作得很好,它可以调用一个封闭的虚拟通用方法。不幸的是,与委托不同,此类型绑定到特定方法。然而,令人头疼的是运行时不允许您创建使用 ldvirtftn 、 ldftn 或 calli 的动态方法> 操作码。
如果您可以使用代码生成,则可以使用表达式树或动态方法来调用该方法。它比直接委托慢一点,但我们谈论的是很小的开销。
Unusually if you really need this and don't mind throwing too much infrastructure at the problem, you can use
ldvirtftn
andcalli
.It seems very strange to me as that's what I thought was what a delegate did behind the scene basically do the following...
Ldvirtftn
does a look up to figure out the function pointer to be invoked for this particular method. 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. 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
, orcalli
opcode.If you are okay with code generation you can use expression trees or dynamicmethod to just invoke the method. It's a little slower than a direct delegate but we're talking a tiny overhead.
Microsoft已回答这是一个已知问题,CLR 无法做到这一点,但在当前版本的 .NET 中无法解决。正如我在那里解释的那样,仍然不清楚为什么这是不可能的。由于某种原因,开放委托不得重用 CLR 中其他地方使用的调度逻辑,这对我来说似乎很奇怪。
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. 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.