Я заметил, что параметры, передаваемые глобальным функциям, всегда используют «вызов», но вызовы перегруженных функций-членов всегда используют «вызов».
Почему он всегда вызывает перегруженную функцию, относящуюся к базовому классу, а не к производному классу, когда этот класс передается в глобальную функцию (Print())?
Похоже, он выбирает, какую перегруженную функцию Print запускать во время компиляции, есть ли что-то особенное, что я сделал, чтобы он решил, что это не должно решаться во время выполнения?
Вот некоторый код, который демонстрирует, что я имею в виду:
Module Module1
Class BaseClass
Friend Overridable Sub Print()
Console.WriteLine("BaseClass.Print")
End Sub
End Class
Class DerivedClass
Inherits BaseClass
Friend Overrides Sub Print()
Console.WriteLine("DerivedClass.Print")
End Sub
End Class
Sub Print(iObject As Object)
Console.WriteLine("Object")
End Sub
Sub Print(iClass1 As BaseClass)
Console.WriteLine("BaseClass")
End Sub
Sub Print(iClass2 As DerivedClass)
Console.WriteLine("DerivedClass")
End Sub
Sub Main()
Dim tBaseClass As New BaseClass
Dim tDerivedClass As New DerivedClass
Dim tBaseClassRef As BaseClass
Dim tObjPtr As Object
Console.WriteLine()
Console.WriteLine("Test 1")
Console.WriteLine()
'in IL it always uses callvirt for an overloaded member function
'from MSDN: The callvirt instruction calls a late-bound method on an object.
' That is, the method is chosen based on the runtime type of obj rather than the compile-time class visible in the method pointer.
'prints "BaseClass.print"
'callvirt instance void Overloading.Module1/BaseClass::Print()
tBaseClass.Print()
'in IL it uses "call", even though Print() is overloaded? Why is this?
'We slip by here because this type is not late bound
'prints "BaseClass"
'call void Overloading.Module1::Print(class Overloading.Module1/BaseClass)
Print(tBaseClass)
Console.WriteLine()
Console.WriteLine("Test 2")
Console.WriteLine()
'prints "DerivedClass.print"
'callvirt instance void Overloading.Module1/BaseClass::Print()
tDerivedClass.Print()
'call works out okay here too because we're still not late bound
'prints "DerivedClass"
'call void Overloading.Module1::Print(class Overloading.Module1/BaseClass)
Print(tDerivedClass)
Console.WriteLine()
Console.WriteLine("Test 3")
Console.WriteLine()
tBaseClassRef = tBaseClass
tBaseClassRef.Print()
'prints "BaseClass.print"
'callvirt instance void Overloading.Module1/DerivedClass::Print()
'call took our word for it that tBaseClassRef is BaseClass typed
'which is correct
Print(tBaseClassRef)
'prints "BaseClass"
'call void Overloading.Module1::Print(class Overloading.Module1/DerivedClass)
Console.WriteLine()
Console.WriteLine("Test 4")
Console.WriteLine()
tBaseClassRef = tDerivedClass
tBaseClassRef.Print()
'prints "DerivedClass.print"
'IL_0098: callvirt instance void Overloading.Module1/BaseClass::Print()
'Callvirt correctly handles our tBaseClass having a derived class's type
Print(tBaseClassRef)
'prints "BaseClass" <!>
'IL_009f: call void Overloading.Module1::Print(class Overloading.Module1/BaseClass)
'"Call" is ill-equipped to handle our Derived class
Console.WriteLine()
Console.WriteLine("Test 5")
Console.WriteLine()
tObjPtr = tDerivedClass
tObjPtr.Print()
'(I don't expect this to work, but the error message surprised me)
' -- unhandled exception -- "Public member 'Print' on type 'DerivedClass' <??> not found.
'IL instructions: http://en.wikipedia.org/wiki/List_of_CIL_instructions
'a. where is it getting DerivedClass from for the exception?
'b. Why did LateCall know what type it was, but was unable to find the method?
' [ILDASM]
' IL_00be: ldloc.3 //Load local [3] object tObjPtr)
' IL_00bf: ldnull // push NULL to stack (no string)
' IL_00c0: ldstr "Print" // push string object for literal string -- is this what we're using as our object?
' IL_00c5: ldc.i4.0 //Push False
' IL_00c6: newarr [mscorlib]System.Object
' IL_00cb: ldnull // no string array
' IL_00cc: ldnull // no class array
' IL_00cd: ldnull // no bool array
' IL_00ce: ldc.i4.1 //Push True
' IL_00cf: call object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateCall(object,
' class [mscorlib]System.Type,
' string,
' object[],
' string[],
' class [mscorlib]System.Type[],
' bool[],
' bool)
Console.WriteLine("Marker") 'divider so I can figure out what it is in idasm
Print(tObjPtr)
' prints "object"
'again, why use "call" instead of "callvirt"? There is a more specific definition of Print that can handle this
'[ILDASM]
' IL_00e0: ldloc.3
' IL_00e1: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
' IL_00e6: call void Overloading.Module1::Print(object)
End Sub
End Module
РЕДАКТИРОВАТЬ: Также, пожалуйста, поделитесь своими источниками, я хотел бы узнать больше о том, как работает IL. Это использует .NET 4.0, если это имеет значение.
Print
наtObjPtr
завершается ошибкой, посколькуPrint
вDerivedClass
равноFriend
, а неPublic
. - person jrh   schedule 06.01.2017