【问题标题】:Delegates in .NET: how are they constructed?.NET 中的代表:它们是如何构造的?
【发布时间】:2011-01-27 11:26:09
【问题描述】:

在检查 C# 和 .NET 中的委托时,我注意到一些有趣的事实:

在 C# 中创建委托会创建一个从 MulticastDelegate 派生的类,并带有构造函数:

.method public hidebysig specialname rtspecialname instance 
   void .ctor(object 'object', native int 'method') runtime managed { }

意味着它需要实例和指向方法的指针。然而,在 C# 中构造委托的语法表明它有一个构造函数

new MyDelegate(int () target)

我可以将int () 识别为函数实例(int *target() 将是 C++ 中的函数指针)。所以很明显,C#编译器从函数名定义的方法组中挑选出正确的方法并构造委托。所以第一个问题是,C# 编译器(或者准确地说是 Visual Studio)从哪里选择这个构造函数签名?我没有注意到任何特殊属性或可以区分的东西。这是某种编译器/视觉工作室的魔法吗?如果不是,T (args) target 构造在 C# 中是否有效?我没有设法得到任何东西来编译,例如:

int() 目标 = 我的方法;

无效,对MyMetod 执行任何操作也是如此,例如在上面调用.ToString()(这确实有些道理,因为这在技术上是一个方法group,但我想应该可以通过强制转换显式选择一个方法,例如(int())MyFunction。那么所有这些纯粹的编译器魔法是什么?通过反射器查看构造揭示了另一种语法:

Func CS$1$0000 = new Func(null, (IntPtr) Foo);

这与反汇编的构造函数签名一致,但无法编译!

最后一个有趣的注意事项是 DelegateMulticastDelegate 类还有另一组构造函数:

.method 系列 hidebysig specialname rtspecialname 实例 void .ctor(class System.Type target, string 'method') cil managed

从实例和方法指针到类型和字符串方法名称的转换发生在哪里?这可以通过自定义委托构造函数签名中的runtime managed 关键字来解释,即运行时是否在这里完成它的工作?

编辑:好的,所以我想我应该重新表述我想通过这个问题表达的意思。基本上,我建议在委托构造中不仅有 C# 编译器/CLR 魔法,还有一些 Visual Studio 魔法,因为 Intellisense 在建议构造函数参数时会翻转一些新语法,甚至隐藏其中一个它们(例如,反射器不使用这种语法和构造器)。

我想知道这个断言是否正确,函数实例语法在 C# 中是否有更深层次的含义,或者它只是 Visual Studio 魔术部分为清楚起见而实现的某种常量格式(这是有道理的,因为它看起来像无效C#) ?简而言之,如果我在实现 Intellisense,我应该为代表做一些魔术,还是可以通过一些巧妙的机制构建建议?

最终编辑:所以,普遍的共识是这确实是 VS 魔法。看到这种 VS 行为的其他示例(请参阅 Marc Gravell 的评论)让我相信确实如此。

【问题讨论】:

  • CS$1$0000 是反编译器生成的,从不编译。
  • 我知道,名字不是这里的重点,是新的结构不能编译。
  • 是的,只是想指出。

标签: c# .net delegates clr .net-internals


【解决方案1】:

这只是一个猜测,所以如果我错了请不要开枪,但我认为 Intellisense 正在从委托上定义的 Invoke 方法获取目标方法的签名。反射器清楚地表明System.Action<T>上的Invoke方法是:

[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
public virtual void Invoke(T obj);

这与 Intellisense 提供的签名建议相同。神奇的是 Intellisense,当发现委托类型时,会查看 Invoke 方法并建议一个构造函数,该构造函数接受与其匹配的目标。

【讨论】:

  • 是的,签名是 Invoke 方法的签名,但是 syntax VS 显示的 C# 无效!
  • @Saulius:VS 显示的工具提示显示了目标方法的纯 签名,而不是所有因目标而异的额外信息(如参数名称和可访问性修饰符)。正如 Marc 指出的那样,这是一种特殊情况,即 Intellisense 会吐出一些伪代码,而不是从程序集的元数据中读取的内容。
【解决方案2】:

您首先定义委托(这是 Visual Studio 知道目标方法签名的方式):

delegate void MyDelegate();

然后你像这样构造委托实例:

MyDelegate method = new MyDelegate({method name});

// If this was the method you wanted to invoke:
void MethodToInvoke()
{
    // do something
}

MyDelegate method = new MyDelegate(MethodToInvoke);

C# 自动选择与委托签名匹配的方法。

编辑:当 Visual Studio 的 Intellisense 向您显示 int () target 建议时,它向您展示了您可以使用的 C# 方法的签名。 C# 编译器将 C# 表示形式转换为 IL。 IL 实现看起来会有所不同,因为 IL 不是 C 风格的语言,并且 C# 编译器提供语法糖来抽象出实现细节。

【讨论】:

  • 我认为他的意思是从低层次的角度来看。
  • @Saulius:int () target 本身不是有效的 C#,但它们如何代表 Intellisense 中的目标?
  • .. 这就是问题所在 - 他们必须展示 something,所以他们是否只是为了清晰而展示了一些编造的语法(仅针对代表的 Visual Studio 魔术),或者一般有更深的含义吗? :)
  • @Saulius:为了清晰起见,我认为这只是一种虚构的语法。
【解决方案3】:

第一个参数是从对象引用中解析出来的(或者 null 用于静态方法);那里没有魔法。

Re 是 second 参数,但是 - 它是一个非托管指针(本机 int);简而言之,没有替代的 direct C# 语法可以使用此构造函数 - 它使用特定的 IL 指令 (ldftn) 从元数据中解析函数。但是,您可以使用Delegate.CreateDelegate 通过反射创建委托。你也可以使用 IL emit(DynamicMethod 等),但这并不好玩。

【讨论】:

  • 好的,所以 C# 不支持方法指针,但是,类似“函数实例”的语法从何而来?我理解从双参数构造函数到单参数 c-tor 作为编译器魔法,但是 Visual Studio 从哪里得到建议单参数构造函数的想法(我在其他任何地方都找不到)?
  • @Saulius - 因为这是语言规范的要求;它没有谈论构造函数,但它指出语法是(例如)new EventHandler(someObject.SomeMethod); - 我算一个“输入”,即使编译器对其进行了一些魔术以生成 IL。
  • @Marc Gravell:那么我对 Visual Studio 魔法的断言如何(查看帖子的编辑)?这部分是困扰我的部分 - 当然只是出于好奇:)
  • Intellisense 实际上对生成的 IL 一无所知。它使用符号数据库和任何可用的 XML 文档 cmets 来理解它在程序中找到的符号。当您遇到委托参数时,Intellisense 知道具体的委托定义,因此它可以显示适当的有用提示。这是 Visual Studio 的一项功能,它独立于编译器提供的使委托更易于使用的便利功能。
  • @Dan Bryant:Visual Studio 不需要用于签名的文档 xml 文件,仅用于 cmets - 我假设签名来自对程序集的反映,因为这就是我所要做的添加到项目以在其上使用智能感知。而且由于我一直将系统程序集视为普通程序集,因此发现这种不一致我有点惊讶。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-01
  • 1970-01-01
  • 2017-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多