2

背景:我正在研究特定于域的 CLI 实现。这个实现有望使用 Mono 或其部分包含的 BCL。它不使用 Mono 的任何其他部分——它从头开始实现 CLR/VES 本身。现在我正在研究 CLR 本身和 BCL 之间的“边界层”,因此我可以确保我的 CLR 将按照 BCL 的预期方式运行。

我在 Mono BCL 源代码中遇到了一些我难以理解的东西。它在执行中System.Delegate

        ...
        private object m_target;
        ...
        public MethodInfo Method {
            ...
        }
        ...

#if NET_2_0
        public object DynamicInvoke (params object[] args)
#else
        public object DynamicInvoke (object[] args)
#endif
        {
            return DynamicInvokeImpl (args);
        }

        protected virtual object DynamicInvokeImpl (object[] args)
        {
            if (Method == null) {
                Type[] mtypes = new Type [args.Length];
                for (int i = 0; i < args.Length; ++i) {
                    mtypes [i] = args [i].GetType ();
                }
                method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
            }

#if NET_2_0
            if ((m_target != null) && Method.IsStatic) {
                // The delegate is bound to m_target
                if (args != null) {
                    object[] newArgs = new object [args.Length + 1];
                    args.CopyTo (newArgs, 1);
                    newArgs [0] = m_target;
                    args = newArgs;
                } else {
                    args = new object [] { m_target };
                }
                return Method.Invoke (null, args);
            }
#endif

            return Method.Invoke (m_target, args);
        }

我在看DynamicInvokeImpl。第一点很清楚:它是MethodInfo根据被调用的签名来获取的。

事情在第二次之后变得奇怪#if NET_2_0。看起来如果提供了一个目标对象,并且被调用的方法是 static,它会向参数添加一个“this”指针!

它继续将这个可能修改过的参数列表传递给MethodInfo.Invoke. 我查看了该方法的 MSDN 文档,似乎很清楚即使调用实例方法,也不应该包含“this”指针 - 更不用说为静态方法添加一个了!

请记住,这不是调用 Mono 的某些内部部分,它可以以任何他们想要的方式工作 - 它调用了一个定义良好的公共方法System.Reflection.MethodInfo

我知道 CLR 必须将隐藏参数(通常是类型句柄)插入到泛型类的静态方法调用中。但是,1)这是一个“this”引用,而不是类型句柄,2)代码似乎是在批发,而不仅仅是针对泛型类,以及 3)类似的事情将在较低级别完成,而不是由的来电者MethodInfo.Invoke

任何线索这里发生了什么?

4

1 回答 1

0

静态方法的委托何时会有目标?

我认为普通的 C# 或 VB.NET 不会发生这种情况。但是,有一个重载Delegate.CreateDelegate说:“使用指定的第一个参数创建表示指定静态或实例方法的指定类型的委托。” 显然,我强调。

阅读名为 的参数的描述firstArgument,它说:“委托绑定到的对象,或null将方法视为staticShared在 Visual Basic 中)。”但我们在 OP 中看到的代码表明情况并非如此。

(顺便说一句,我认为这是一个错误,应该说!Method.IsStatic。)这已被记录为自 .NET 2.0 以来的一项功能。

这段代码表明我们可以这样做:

Module Module1
Class DelegateTest
    Shared Sub Test(ByVal arg1 As Object, ByVal arg2 As Object)
        Console.WriteLine("arg1: {0}: {1}", If(arg1 IsNot Nothing, arg1.GetType.Name, "Nothing"), arg1)
        Console.WriteLine("arg2: {0}: {1}", If(arg2 IsNot Nothing, arg2.GetType.Name, "Nothing"), arg2)
    End Sub
End Class
Delegate Sub TestDelegate(ByVal arg1 As Object, ByVal arg2 As Object)
Delegate Sub TestDelegate2(ByVal arg2 As Object)
Sub Main()
    Dim a As TestDelegate = AddressOf DelegateTest.Test
    a.DynamicInvoke("A arg1", "A arg2")
    Dim dt = New DelegateTest
    Dim b As New TestDelegate(AddressOf dt.Test)
    b.DynamicInvoke("B Arg1", "B Arg2")
    a = DirectCast(TestDelegate.CreateDelegate(GetType(TestDelegate), GetType(DelegateTest).GetMethod("Test", Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public)), TestDelegate)
    a.DynamicInvoke("A Arg1", "A Arg2")
    Dim c As TestDelegate = DirectCast(TestDelegate.CreateDelegate(GetType(TestDelegate), Nothing, GetType(DelegateTest).GetMethod("Test", Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public)), TestDelegate)
    c.DynamicInvoke("C ARG1", "C ARG2")

    Dim d As TestDelegate2 = DirectCast(TestDelegate2.CreateDelegate(GetType(TestDelegate2), "D A1", GetType(DelegateTest).GetMethod("Test", Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public)), TestDelegate2)
    d.DynamicInvoke("D A2")
    d("D A2") ' Just confirming there's nothing special about DynamicInvoke here.

    If Debugger.IsAttached Then _
      Console.ReadLine()

End Sub

End Module

这编译并输出:

arg1:字符串:A arg1
arg2:字符串:A arg2
arg1:字符串:B Arg1
arg2:字符串:B Arg2
arg1:字符串:A Arg1
arg2:字符串:A Arg2
arg1:字符串:C ARG1
arg2:字符串:C ARG2
arg1:字符串:D A1
arg2:字符串:D A2
arg1:字符串:D A1
arg2:字符串:D A2

  • a只是一个默认的静态委托,并用.CreateDelegate.
  • b只是显示不需要的实例被忽略(并且有一个警告)。
  • c尝试使用有趣的重载,但它要求null( Nothing) 在运行时不要在创建时引发方法绑定错误。
  • d显示了我们看到代码启用的重载,我确认它不限于DynamicInvoke.

这是使用 VS2k8 使用 .NET 3.5/2.0 编译的。

于 2014-04-01T16:00:45.607 回答