在C#中将Dynamic和var转换为Object

52 c# dynamic

考虑这些功能:

static void Take(object o)
{
    Console.WriteLine("Received an object");
}

static void Take(int i)
{
    Console.WriteLine("Received an integer");
}
Run Code Online (Sandbox Code Playgroud)

当我以Take这种方式调用函数时:

var a = (object)2;
Take(a);
Run Code Online (Sandbox Code Playgroud)

我明白了:Received an object

但如果称之为:

dynamic b = (object) 2;
Take(b);
Run Code Online (Sandbox Code Playgroud)

我明白了:Received an integer

两个参数(a&b)都被转换为object.但为什么编译器有这种行为?

Ken*_*Kin 55

var只是一种语法糖,让RHS决定类型.

在你的代码中:

var a = (object)2;
Run Code Online (Sandbox Code Playgroud)

相当于:

object a = (object)2;
Run Code Online (Sandbox Code Playgroud)

你得到一个对象,因为你装箱2了一个对象.

对于dynamic,您可能需要查看使用类型动态.请注意,类型是静态类型,但动态类型的对象会绕过静态类型检查,即您指定的类型:

dynamic b = (object) 2;
Run Code Online (Sandbox Code Playgroud)

被绕过,并在运行时解析它的真实类型.


对于它在运行时如何解决,我相信它比你想象的要复杂得多.

假设您有以下代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我把完整的IL(调试配置)放在我的答案的后面.

对于这两行:

var a=(object)2;
Take(a);
Run Code Online (Sandbox Code Playgroud)

IL只是:

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)
Run Code Online (Sandbox Code Playgroud)

但对于这两个:

dynamic b=(object)2;
Take(b);
Run Code Online (Sandbox Code Playgroud)

IL_000fIL_007aTestMethod.它不会直接调用Take(object)or Take(int),而是像这样调用方法:

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);
Run Code Online (Sandbox Code Playgroud)

完整的IL 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
Run Code Online (Sandbox Code Playgroud)


Siv*_*ran 15

动态:

  1. dynamic 是一个 Dynamically typed
  2. 动态类型 - 这意味着声明的变量类型由编译器在运行时决定.

VAR:

  1. var 是一个 Statically typed
  2. 静态类型 - 这意味着声明的变量类型由编译器在编译时决定.

通过这种方式,您可以看到重载解析在运行时发生dynamic.

所以变量b保持为int

dynamic b = (object) 2;
Take(b);
Run Code Online (Sandbox Code Playgroud)

这就是Take(b);电话的原因Take(int i)

static void Take(int i)
    {
        Console.WriteLine("Received an integer");
    }
Run Code Online (Sandbox Code Playgroud)

但在这种情况下var a = (object)2,变量a保持为'对象'

var a = (object)2;
Take(a);
Run Code Online (Sandbox Code Playgroud)

这就是为什么Take(a); 电话Take(object o)

static void Take(object o)
    {
        Console.WriteLine("Received an object");
    }
Run Code Online (Sandbox Code Playgroud)

  • 你对`dynamic`的解释不太正确:变量*的类型是*`object`.但是,当在表达式中使用该变量时,将导致在运行时调用编译器,并根据存在的运行时类型执行表达式的计算(粗略地说). (8认同)
  • 是我还是实际上没有回答这个问题? (4认同)
  • @DimitarDimitrov:对象实例在运行时只能是一种类型.不只是`dynamic`而且`object a =(object)2; a.GetType();`将在_runtime_给你`System.Int32`.只是第一个重载决议是由编译器在编译时完成的,这就是为什么它解析为`object`重载. (2认同)
  • "编译器在运行时"如何在运行时调用编译器,它可能是解析该动态变量类型的C#VM或C#解释器. (2认同)