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。呼び出された署名に基づいて を取得しています。

2回目以降はおかしくなる#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メソッドをstatic( SharedVisual 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 に答える