C#为什么我可以在常量上调用对象扩展方法?

San*_*mar 2 .net c#

在C#中为什么我可以打电话

var intStr = 1.ToString();
var strStr = "1".ToString();
Run Code Online (Sandbox Code Playgroud)

1和"1"是常量文字,为什么object.ToString()不导致错误?

Eri*_*ert 7

为什么我可以在常量上调用对象扩展方法?

那些不是扩展方法.

1并且"1"是常数所以为什么ToString()不导致错误?

你的问题在没有解释为什么你认为这首先应该成为一个问题的问题时会问"为什么不" .在将来询问此表格的问题时,请说明为什么您认为该行动应该被禁止.

所以让我们试着读懂你的想法.这是你可能会问的一个问题:

1"1"文字,但点的左侧必须是一个符号,像x,所以为什么 1.ToString()不造成的错误?

假设不正确.成员访问点左侧的东西必须是表达式.这种表达有限制; 例如,它不能是返回void的方法调用.但它没有限制它是文字.

这是你可能会问的另一个问题:

1值类型常量,值类型的方法的接收者必须是变量,因为this方法中ref的变量是变量.那么为什么 1.ToString()不造成错误呢?

如果方法调用的接收者是值类型并且this必须是ref类型但接收器表达式未被分类为变量,则编译器将该值复制到临时变量并将a传递ref给该变量.

这意味着如果方法改变变量,则突变会丢失,因为它是在副本上执行的.这是可变值类型是一种不好的做法的另一个原因.不小心丢失突变很容易!

这是你可能会问的另一个问题:

1.是float,double或decimal字面的开头; 接下来的事情应该是一个数字.那么为什么 1.ToString()不造成错误呢?

这个问题做出了错误的假设.词法分析器检查点后面的东西是否是数字; 如果它然后它保持lexing文字作为浮点数,双精度或小数.(它将由后缀确定,如果有的话.)如果点后面的东西不是数字,那么词法分析器会将点作为成员访问点,并开始为一个新的令牌ToString.


Mat*_*son 5

它们是从常量文字(在程序文本中)初始化的,但它们实际上是对象(因为它们在逻辑上被视为对象),并且所有对象都实现 ToString()

1类型也是如此System.Int32.

因此你可以打电话ToString()给他们.

为此生成的IL是这样的:

L_0001: ldc.i4.1 
L_0002: stloc.1 
L_0003: ldloca.s CS$0$0000
L_0005: call instance string [mscorlib]System.Int32::ToString()
Run Code Online (Sandbox Code Playgroud)

请注意,这ldc.i4.1是一个特殊的指令,它将System.Int32值1推入堆栈.具体而言,这是实际"创建" System.Int32价值对象的指令.

另请注意,尽管System.Int32是值类型,但它也被视为对象,因此以下语句始终为true:

bool isObject = (1 is object);
Run Code Online (Sandbox Code Playgroud)

  • 小心......文字`1`是一个int*值*,在调用`ToString()`之前将其装箱到一个对象. (3认同)
  • 调用`int.ToString()`不做任何装箱 - 看到这个答案:http://stackoverflow.com/questions/6423452/boxing-and-unboxing-in-int-and-string注意,如果它是拳击,IL将包含`box`指令.它不会为`int`做装箱,因为`int`会覆盖`ToString()`,这会阻止拳击发生. (2认同)
  • @CSJ:有趣的事实:`1.GetType()`确实包装了int.`GetType`不是虚拟的,因此不会在`Int32`上重写,因此`this`必须是`object`类型,所以它是盒装的.当然,首先要做到这一点是愚蠢的,因为`typeof(int)`更清楚. (2认同)