Mempertimbangkan fungsi-fungsi ini:
static void Take(object o)
{
Console.WriteLine("Received an object");
}
static void Take(int i)
{
Console.WriteLine("Received an integer");
}
Ketika saya sebut Mengambil
fungsi dengan cara ini:
var a = (object)2;
Take(a);
Saya mendapatkan :Menerima sebuah objek
Tapi jika menyebutnya seperti:
dynamic b = (object) 2;
Take(b);
Saya mendapatkan:Menerima integer
Kedua parameter (a
& b
) adalah pemain untuk objek
. Tapi mengapa compiler memiliki perilaku ini?
var
hanya sintaksis gula untuk membiarkan tipe yang akan diputuskan oleh RHS.
Dalam kode anda:
var a = (object)2;
setara dengan:
object a = (object)2;
Anda mendapatkan sebuah objek, karena anda kemas 2
untuk sebuah objek.
Untuk dinamis
, anda mungkin ingin melihat-lihat di Menggunakan Jenis dinamis. Perhatikan bahwa tipe adalah tipe static, tapi sebuah objek dari tipe yang dinamis melewati statis jenis pemeriksaan, yaitu, jenis yang anda ditentukan dari:
dynamic b = (object) 2;
dilewati, dan jenis nyata dari hal ini diselesaikan pada saat runtime.
Untuk cara's diselesaikan pada saat runtime, saya percaya itu's jauh lebih rumit dari yang anda bayangkan ..
Katakanlah anda memiliki kode berikut:
public static class TestClass {
public static void Take(object o) {
Console.WriteLine("Received an object");
}
public static void Take(int i) {
Console.WriteLine("Received an integer");
}
public static void TestMethod() {
var a=(object)2;
Take(a);
dynamic b=(object)2;
Take(b);
}
}
dan aku meletakkan penuh IL(dari konfigurasi debug) di bagian belakang dari jawaban saya.
Untuk dua baris:
var a=(object)2;
Take(a);
IL hanya:
IL_0001: ldc.i4.2
IL_0002: box [mscorlib]System.Int32
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: call void TestClass::Take(object)
Tapi untuk yang dua ini:
dynamic b=(object)2;
Take(b);
dari IL_000f
untuk IL_007a
dari TestMethod
. Itu doesn't sebut Mengambil(objek)
atau Mengambil(int)
secara langsung, tetapi memanggil metode seperti cara ini:
object b = 2;
if (TestClass.<TestMethod>o__SiteContainer0.<>p__Site1 == null)
{
TestClass.<TestMethod>o__SiteContainer0.<>p__Site1 = CallSite<action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Take", null, typeof(TestClass), new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
TestClass.<TestMethod>o__SiteContainer0.<>p__Site1.Target(TestClass.<TestMethod>o__SiteContainer0.<>p__Site1, typeof(TestClass), b);
Penuh IL dari TestClass
:
.class public auto ansi abstract sealed beforefieldinit TestClass
extends [mscorlib]System.Object
{
// Nested Types
.class nested private auto ansi abstract sealed beforefieldinit '<TestMethod>o__SiteContainer0'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Fields
.field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> '<>p__Site1'
} // end of class <TestMethod>o__SiteContainer0
// Methods
.method public hidebysig static
void Take (
object o
) cil managed
{
// Method begins at RVA 0x2050
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Received an object"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method TestClass::Take
.method public hidebysig static
void Take (
int32 i
) cil managed
{
// Method begins at RVA 0x205e
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Received an integer"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method TestClass::Take
.method public hidebysig static
void TestMethod () cil managed
{
// Method begins at RVA 0x206c
// Code size 129 (0x81)
.maxstack 8
.locals init (
[0] object a,
[1] object b,
[2] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000
)
IL_0000: nop
IL_0001: ldc.i4.2
IL_0002: box [mscorlib]System.Int32
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: call void TestClass::Take(object)
IL_000e: nop
IL_000f: ldc.i4.2
IL_0010: box [mscorlib]System.Int32
IL_0015: stloc.1
IL_0016: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>o__SiteContainer0'::'<>p__Site1'
IL_001b: brtrue.s IL_0060
IL_001d: ldc.i4 256
IL_0022: ldstr "Take"
IL_0027: ldnull
IL_0028: ldtoken TestClass
IL_002d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0032: ldc.i4.2
IL_0033: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_0038: stloc.2
IL_0039: ldloc.2
IL_003a: ldc.i4.0
IL_003b: ldc.i4.s 33
IL_003d: ldnull
IL_003e: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_0043: stelem.ref
IL_0044: ldloc.2
IL_0045: ldc.i4.1
IL_0046: ldc.i4.0
IL_0047: ldnull
IL_0048: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_004d: stelem.ref
IL_004e: ldloc.2
IL_004f: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Type>, class [mscorlib]System.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_0054: call class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
IL_0059: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>o__SiteContainer0'::'<>p__Site1'
IL_005e: br.s IL_0060
IL_0060: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>o__SiteContainer0'::'<>p__Site1'
IL_0065: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>>::Target
IL_006a: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>o__SiteContainer0'::'<>p__Site1'
IL_006f: ldtoken TestClass
IL_0074: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0079: ldloc.1
IL_007a: callvirt instance void class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>::Invoke(!0, !1, !2)
IL_007f: nop
IL_0080: ret
} // end of method TestClass::TestMethod
} // end of class TestClass
dinamis
adalah Dinamis diketik
var
adalah Statis diketik
Dengan ini, anda melihat bahwa Overload resolusi terjadi pada jangka waktu untuk dinamis
.
Jadi variabel b
memegang int
dynamic b = (object) 2;
Take(b);
Yang's alasan mengapa (b);
panggilan Mengambil(int i)
static void Take(int i)
{
Console.WriteLine("Received an integer");
}
Tapi dalam kasus var a = (objek)2
, variabel a
memegang sebagai 'objek'
var a = (object)2;
Take(a);
Yang's alasan mengapa Mengambil(a); panggilan Mengambil(object o)
static void Take(object o)
{
Console.WriteLine("Received an object");
}
Kotak argumen integer resolusi terjadi pada waktu kompilasi. Berikut ini adalah IL:
IL_000d: box [mscorlib]System.Int32
IL_0012: stloc.0
IL_0013: ldloc.0
IL_0014: call void ConsoleApp.Program::Take(object)
Anda dapat melihat itu adalah memutuskan untuk objek
yang berlebihan pada waktu kompilasi itu sendiri.
Ketika anda menggunakan dinamis
- runtime pengikat datang ke dalam gambar. dinamis
tidak hanya dapat mengatasi untuk dikelola C# benda-benda, tetapi juga untuk non-managed benda-benda seperti COM benda-benda atau objek JavaScript yang diberikan runtime binder ada untuk objek tersebut.
Bukannya menunjukkan IL, I'll show decompiled kode (mudah dibaca):
object obj3 = 2;
if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
<Main>o__SiteContainer0.<>p__Site1 = CallSite<action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Take", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
}
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, typeof(Program), obj3);
Anda melihat bahwa Mengambil
metode ini diselesaikan pada saat runtime oleh runtime pengikat dan bukan oleh compiler. Jadi, itu akan menyelesaikan itu untuk jenis sebenarnya.
Jika anda melihat pada C# spesifikasi:
1.6.6.5 Metode overloading
Metode overloading memungkinkan beberapa metode dalam kelas yang sama untuk memiliki nama yang sama asalkan mereka memiliki tanda tangan unik. Ketika menyusun sebuah doa dari kelebihan beban metode, compiler menggunakan overload resolusi untuk menentukan metode spesifik untuk memohon.
Dan:
7.5.4 Compile-time memeriksa dinamis yang berlebihan resolusi
Untuk paling dinamis terikat operasi himpunan kandidat untuk resolusi adalah tidak diketahui pada compiletime. Dalam kasus-kasus tertentu, namun calon mengatur diketahui pada waktu kompilasi:
- metode Statis panggilan dengan dinamis argumen
- Contoh pemanggilan metode di mana penerima tidak dinamis ekspresi
- Pengindeks panggilan di mana penerima tidak dinamis ekspresi
- Pembina panggilan dengan dinamis argumen
Dalam kasus ini terbatas waktu kompilasi pemeriksaan dilakukan untuk masing-masing kandidat untuk melihat apakah salah satu dari mereka yang mungkin bisa berlaku pada saat run-time
Jadi, dalam kasus pertama, var
tidak dinamis, overload resolusi akan menemukan metode yang berlebihan pada waktu compile.
Tapi dalam kedua kasus, anda menelepon metode statis dengan dinamis argumen, overload resolusi akan menemukan metode yang berlebihan pada saat run-time, bukan
Untuk membantu memahami jenis resolusi dalam kasus anda untuk variabel dinamis.
(B); //Lihat di sini jenis b ketika u hover mouse di atas itu, akan int
yang jelas berarti bahwa kode:
dinamis b = (objek)2
tidak mengkonversi 2 untuk sebuah objek ketika ditugaskan untuk variabel dinamis dan b tetap int
Berikutnya, komentar anda overload Take(int i)
metode dan
kemudian dimasukkan break point pada baris (b)
(sama: type b
masih
int
), tetapi ketika anda menjalankannya, anda akan melihat dicetak nilai adalah: Menerima
objek.
Sekarang, mengubah anda dinamis
variabel panggilan untuk kode di bawah ini:
Mengambil((objek)b); //sekarang cetakan "Menerima sebuah objek"
dinamis b = (panjang)2;
(B); // sekarang cetakan yang Diterima objek karena tidak ada metode yang berlebihan yang menerima panjang dan pencocokan terbaik yang berlebihan adalah salah satu yang menerima sebuah
objek.
Ini adalah karena: terbaik cocok jenis ini diselesaikan untuk dinamis variabel sesuai dengan nilai yang dimilikinya pada saat runtime dan juga terbaik sesuai metode yang berlebihan untuk disebut diselesaikan pada saat runtime untuk variabel dinamis .
Dalam kasus pertama var berarti objek
jadi Mengambil(object o)
disebut. Dalam kedua kasus anda menggunakan dinamis
jenis dan meskipun bahwa anda've kotak int
itu masih memiliki informasi tentang hal itu's type -int
jadi yang terbaik pencocokan metode ini disebut.