【问题标题】:C# object looks like dynamic typeC# 对象看起来像动态类型
【发布时间】:2013-02-19 20:43:24
【问题描述】:

一直在使用 4.0 DLR 并正在将动态与对象进行比较并遇到了这个问题:

代码:

object x = 10;
            Console.WriteLine("x = {0} and is a {1}.\n", x, x.GetType());
            x = (int)x + 3;
            Console.WriteLine("x = {0} and is a {1}.\n", x, x.GetType());
            x = x + "a";
            Console.WriteLine("x = {0} and is a {1}.\n", x, x.GetType());

结果:

x = 10 并且是 System.Int32。

x = 13 并且是 System.Int32。

x = 13a 并且是 System.String。

在我看来,对象似乎试图在运行时(动态)使对象适应类型。但是,如果我不在第 3 行将 x 转换为 int,它会给我一个编译器区域,这对于静态类型来说似乎是正确的。但随后它允许我向 x 添加一个“a”,现在它将它识别为一个字符串。

我错过了什么?

【问题讨论】:

  • 不知道是不是因为String其实是一个Object,而3是base类型。

标签: c# .net dynamic


【解决方案1】:

您错过了这样一个事实,即+ 运算符在应用于字符串时会进行自动转换(通过在不是String 类型实例的操作数上调用.ToString() 方法)。

【讨论】:

    【解决方案2】:

    来自C# Language Specification

    7.8.4 加法运算符

    当一个或两个操作数都是字符串类型时,预定义的加法运算符连接操作数的字符串表示形式。

    • 字符串连接:

      string operator +(string x, string y);
      string operator +(string x, object y);
      string operator +(object x, string y);
      

      二元 + 运算符的这些重载执行字符串连接。如果字符串连接的操作数是null,则替换为空字符串。否则,通过调用从类型 object 继承的虚拟 ToString 方法,将任何非字符串参数转换为其字符串表示形式。如果ToString 返回null,则替换为空字符串。

    【讨论】:

      【解决方案3】:

      请记住,变量(即object)的类型和它所引用的对象的类型是有区别的。

      最后一次转换是允许的,因为System.String 类型定义了一个接受object 作为值之一并对其执行自动ToString() 的运算符。

      运行时或动态操作没有什么好玩的。

      【讨论】:

        【解决方案4】:

        在我看来,对象试图在运行时(动态)使对象适应类型。

        并非如此:它只是锻炼了 .NET 的 object 存储任何类型对象的能力。

        为了成为dynamic,变量应该允许你在没有强制转换的情况下调用任何东西,例如

        dynamic x = "hello";
        dynamic y = x.Substring(0, 2); // The compiler does not complain
        

        如果您尝试使用“普通”object 来完成上述技巧,您会收到来自编译器的愤怒消息。但是,使用dynamic 会将抱怨推迟到运行时,此时它可能会发生,也可能根本不会发生。

        但是它允许我向 x 添加一个“a”,现在它可以将它识别为一个字符串。

        这是编译器的“魔法”:它知道object 有一个ToString,并自动为您插入调用。

        【讨论】:

          【解决方案5】:

          由于C#中的所有类型都派生自object类型,它可以存储任何类型

          object x = 10;
          x = x + "a";
          

          编译器实际上是这样做的。由于object.ToString(),所以当你使用+时,编译器会自动调用它。

          即使我们查看 IL 的代码;

            .locals init ([0] object x)
            IL_0000:  nop
            IL_0001:  ldc.i4.s   10
            IL_0003:  box        [mscorlib]System.Int32
            IL_0008:  stloc.0
            IL_0009:  ldloc.0
            IL_000a:  ldstr      "a"
            IL_000f:  call       string [mscorlib]System.String::Concat(object,
                                                                        object)
          

          String.Concat 方法的返回值是string 值的表示形式。

          来自C# 5.0 Specification

          • 字符串连接:

            string operator +(string x, string y);
            string operator +(string x, object y);
            string operator +(object x, string y);
            

            二元 + 运算符的这些重载执行字符串连接。如果字符串连接的操作数是null,则替换为空字符串。否则,通过调用从类型 object 继承的虚拟 ToString 方法,将任何非字符串参数转换为其字符串表示形式。如果ToString 返回null,则替换为空字符串。

          【讨论】:

          • 有趣的是为什么x + "a" 会在 IL 中生成对Concat 的调用。从输出中可以明显看出它是这样做的。原因在this answer中解释
          • @Servy 事实上,当我第一次看到在 IL 中调用 Concat 时,我也很惊讶。我刚刚反编译了String.Concat(obj, obj),结果在这里; public static string Concat(object arg0, object arg1) { if (arg0 == null) arg0 = (object) string.Empty; if (arg1 == null) arg1 = (object) string.Empty; return arg0.ToString() + arg1.ToString(); } 我认为这个过程使用.ToString() 方法使用String.Concat(obj, obj) 方法。
          【解决方案6】:

          C# 中的所有内容都继承自 objectintstringchar 等都可以。

          当你这样做时:

          object x = 10;
          

          您正在创建一个变量x,然后将其初始化为整数值10。该值是一个int,它只是将boxed 转换为一个对象。

          当您将x 转换为int 时,您将unboxing 转换为它的实际类型。

          查看Boxing and Unboxing 的本指南

          【讨论】:

            【解决方案7】:

            c# 上的所有数据类型都继承自“对象”,因此任何操作的基本行为都取决于运算符和其他对象

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-11-26
              • 1970-01-01
              • 2011-03-27
              • 1970-01-01
              • 2019-10-30
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多