'this' включено в MethodInfo.Invoke для статического метода (от Mono BCL)
Справочная информация: я работаю над реализацией CLI для конкретного домена. Мы надеемся, что эта реализация будет использовать BCL, включенный в Mono или его части. Он не использует никакую другую часть 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
, Похоже, если целевой объект был предоставлен, а вызываемый метод является статическим, он добавляет указатель 'this' к аргументам!
Далее он передает этот возможно измененный список аргументов MethodInfo.Invoke
, Я просмотрел документацию MSDN для этого метода, и кажется довольно ясным, что даже при вызове метода экземпляра вы не должны включать указатель this, не говоря уже о добавлении одного для статического метода!
Имейте в виду, что это не вызов некоторой внутренней части Mono, которая могла бы работать так, как они хотят - это вызов четко определенного публичного метода в System.Reflection.MethodInfo
!
Мне известно, что CLR должен вставлять скрытый аргумент - обычно дескриптор типа - в вызовы статических методов в универсальных классах. Тем не менее, 1) это ссылка "это", а не дескриптор типа, 2) код, кажется, делает это оптом, а не только для общих классов, и 3) что-то подобное будет сделано на более низком уровне, а не вызывающий MethodInfo.Invoke
!
Любая подсказка, что здесь происходит?
1 ответ
Когда у делегата статического метода будет цель?
Я не думаю, что это может случиться с обычным C# или VB.NET. Однако существует перегрузка Delegate.CreateDelegate
что говорит: "Создает делегат указанного типа, который представляет указанный статический или метод экземпляра, с указанным первым аргументом ". с моим акцентом, очевидно.
Чтение описания для аргумента под названием firstArgument
, он говорит: "Объект, к которому привязан делегат, или null
относиться к методу как static
(Shared
в 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: строка: arg1
arg2: строка: arg2
arg1: строка: B Arg1
arg2: строка: B Arg2
arg1: строка: A Arg1
arg2: строка: 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
,
Это скомпилировано с.NET 3.5/2.0 с использованием VS2k8.