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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17291273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:03:39  来源:igfitidea点击:

Casting Dynamic and var to Object in C#

c#dynamic

提问by

Consider these functions:

考虑这些函数:

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

static void Take(int i)
{
    Console.WriteLine("Received an integer");
}

When I call the Takefunction this way:

当我这样调用Take函数时:

var a = (object)2;
Take(a);

I get :Received an object

我得到:Received an object

But if call it like:

但如果这样称呼它:

dynamic b = (object) 2;
Take(b);

I get:Received an integer

我得到:Received an integer

Both parameters (a& b) are cast to object. But why compiler has this behavior?

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

采纳答案by Ken Kin

varis just a syntactic sugar to let the type to be decided by the RHS.

var只是一个语法糖,让类型由RHS决定。

In your code:

在您的代码中:

var a = (object)2;

is equivalent to:

相当于:

object a = (object)2;

You get an object, since you boxed 2to an object.

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

For dynamic, you might want to have a look at Using Type dynamic. Note that The type is a static type, but an object of type dynamic bypasses static type checking, that is, the type you specified of:

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

dynamic b = (object) 2;

is bypassed, and the real type of it is resolved at runtime.

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



For how it's resolved at runtime, I believe it's much more complicated than you can imagine ..

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

Say you have the following code:

假设您有以下代码:

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);
    }
}

and I put the full IL(of debug configuration) at the rear of my answer.

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

For these two lines:

对于这两行:

var a=(object)2;
Take(a);

the IL are only:

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)

But for these two:

但是对于这两个:

dynamic b=(object)2;
Take(b);

are from IL_000fto IL_007aof TestMethod. It doesn't call the Take(object)or Take(int)directly, but invokes the method like this way:

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);


The full IL of TestClass:

的完整 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
dynamic b = (object) 2;
Take(b);
static void Take(int i)
    {
        Console.WriteLine("Received an integer");
    }
00 ) 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

回答by Siva Charan

dynamic:

动态的:

  1. dynamicis a Dynamically typed
  2. Dynamically typed - This means the type of variable declared is decided by the compiler at run time.
  1. dynamic是一个 Dynamically typed
  2. 动态类型 - 这意味着声明的变量类型由编译器在运行时决定。

var:

变量:

  1. varis a Statically typed
  2. Statically typed – This means the type of variable declared is decided by the compiler at compile time.
  1. var是一个 Statically typed
  2. 静态类型 - 这意味着声明的变量类型由编译器在编译时决定。

By this, you see that Overload resolution occurs at run time for dynamic.

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

So variable bholds as int

所以变量b保持为int

var a = (object)2;
Take(a);

That's the reason why Take(b);calls Take(int i)

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

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

But in the case of var a = (object)2, variable aholds as 'object'

但在 的情况下var a = (object)2,变量a保持为“对象”

IL_000d:  box        [mscorlib]System.Int32
IL_0012:  stloc.0
IL_0013:  ldloc.0
IL_0014:  call       void ConsoleApp.Program::Take(object)

That's the reason why Take(a); calls Take(object o)

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

   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);

回答by Guru Stron

In first case var means objectso Take(object o)is called. In second case you use dynamictype and despite that you've boxed your intit still has information about it's type -intso the best matching method is called.

在第一种情况下 var 意味着objectsoTake(object o)被调用。在第二种情况下,您使用dynamictype 并且尽管您已将其装箱,但int它仍然具有有关其类型的信息-int因此调用了最佳匹配方法。

回答by cuongle

If you take a look on C# specification:

如果您查看 C# 规范:

1.6.6.5 Method overloading

1.6.6.5 方法重载

Method overloading permits multiple methods in the same class to have the same name as long as they have unique signatures. When compiling an invocation of an overloaded method, the compiler uses overload resolution to determine the specific method to invoke.

方法重载允许同一类中的多个方法具有相同的名称,只要它们具有唯一的签名即可。编译重载方法的调用时,编译器使用重载决议来确定要调用的特定方法。

And:

和:

7.5.4 Compile-time checking of dynamic overload resolution

7.5.4 动态重载解析的编译时检查

For most dynamically bound operations the set of possible candidates for resolution is unknown at compiletime. In certain cases, however the candidate set is known at compile-time:

  • Static method calls with dynamic arguments

  • Instance method calls where the receiver is not a dynamic expression

  • Indexer calls where the receiver is not a dynamic expression

  • Constructor calls with dynamic arguments

In these cases a limited compile-time check is performed for each candidate to see if any of them could possibly apply at run-time

对于大多数动态绑定操作,可能的解析候选集在编译时是未知的。然而,在某些情况下,候选集在编译时是已知的:

  • 带有动态参数的静态方法调用

  • 接收者不是动态表达式的实例方法调用

  • 接收者不是动态表达式的索引器调用

  • 带有动态参数的构造函数调用

在这些情况下,对每个候选者执行有限的编译时检查,以查看它们中的任何一个是否可能在运行时应用

So, in your first case, varis not dynamic, overload resolutionwill find overload method at compile-time.

所以,在你的第一种情况下,var不是动态的,重载解析会在编译时找到重载方法。

But in your second case, you are calling static method with dynamic arguments, overload resolutionwill find overload method at the run-time instead

但是在第二种情况下,您使用动态参数调用静态方法重载解析将在运行时找到重载方法

回答by VS1

To help understand type resolution in your case for dynamic variables.

帮助理解动态变量的类型解析。

  • First put a break point at the line:

    Take(b); //See here the type of b when u hover mouse over it, will be int

  • 首先在该行放置一个断点:

    Take(b); //See here the type of b when u hover mouse over it, will be int

which clearly means that the code: dynamic b = (object)2does not convert 2 to an object when assigned to a dynamic variable and b remains an int

这清楚地意味着代码:dynamic b = (object)2当分配给动态变量时不会将 2 转换为对象并且 b 仍然是 int

  • Next, comment out your overload of Take(int i)method and then put a break point on line Take(b)(same: type of bis still int) but when you run it, you will see printed value is: Recieved object.

  • Now, change your dynamicvariable call to below code:

    Take((object)b); //It now prints "Received an object"

  • Next, change your call to below code and see what is returned:

    dynamic b = (long)2;

    Take(b); // It now prints Received an object because there is no method overload that accepts a long and best matching overload is one that accepts anobject.

  • 接下来,注释掉您的Take(int i)方法重载,然后在行上放置一个断点Take(b)(相同:b仍然是 类型int)但是当您运行它时,您将看到打印的值为:已接收对象。

  • 现在,将您的dynamic变量调用更改为以下代码:

    Take((object)b); //It now prints "Received an object"

  • 接下来,将您的调用更改为以下代码并查看返回的内容:

    dynamic b = (long)2;

    Take(b); // It now prints Received an object because there is no method overload that accepts a long and best matching overload is one that accepts an目的.

This is because:best matching type is resolved for the dynamic variables according to the value it holds at runtime and also best matching overload method to be called is resolved at runtime for dynamic variables .

这是因为:动态变量的最佳匹配类型是根据它在运行时保存的值来解析的,并且要调用的最佳匹配重载方法也是在运行时为动态变量解析的。

回答by YK1

The boxed integer argument resolution happens at compile time. Here is the IL:

装箱整数参数解析发生在编译时。这是IL:

##代码##

You can see it is resolved to the objectoverload at compile time itself.

你可以看到它object在编译时被解析为重载本身。

When you use dynamic- the runtime binder comes into picture. dynamicnot only can resolve to managed C# objects, but also to non-managed objects like COM objects or JavaScript objects given a runtime binder exists for those objects.

当您使用时dynamic- 运行时绑定器会出现。dynamic不仅可以解析为托管 C# 对象,还可以解析为非托管对象,如 COM 对象或 JavaScript 对象,前提是这些对象存在运行时绑定器。

Instead of showing IL, I'll show decompiled code (easier to read):

我将显示反编译代码(更易于阅读),而不是显示 IL:

##代码##

You see that Takemethod is resolved at runtime by the runtime binder and not by compiler. So, it will resolve it to the actual type.

您会看到该Take方法在运行时由运行时绑定器而不是编译器解析。因此,它会将其解析为实际类型。