【问题标题】:C# Why i can call object extension methods on constants?C# 为什么我可以在常量上调用对象扩展方法?
【发布时间】:2017-05-19 01:54:34
【问题描述】:

在 C# 中为什么我可以调用

var intStr = 1.ToString();
var strStr = "1".ToString();

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

【问题讨论】:

  • 为什么它们会导致错误?为什么它需要与完全相同类型的变量有所不同?另外,ToString 不是扩展方法,它是实例方法。 差异。
  • @Servy 我错误地认为常量与变量不同 :) 并感谢您指出 ToString() 是一个实例方法。
  • 常量不同于变量。变量是存储位置,它们的值可以改变,这就是它们被称为“变量”的原因。常量是不会改变的,这就是它们被称为“常量”的原因。您的问题不清楚的是,为什么您认为 值存储的恒定性将值作为接收者调用方法是否合法相关.既然你没有告诉我们,我会假设你为什么认为我的回答是不正确的。

标签: c# .net


【解决方案1】:

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

那些不是扩展方法。

1"1" 是常量,那么为什么 ToString() 不会导致错误?

你的问题问的是“为什么不”没有解释为什么你认为这首先应该是一个问题。以后在询问这种形式的问题时,请说明您认为应该禁止该操作的原因。

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

1"1"文字,但点的左侧必须是符号,如x,那么为什么是1.ToString()不会导致错误?

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

这是您可以问的另一个问题:

1是一个值类型的常量但是值类型的方法的接收者必须是一个变量,因为方法中的this是一个ref到一个变量.那么为什么1.ToString() 不会导致错误呢?

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

这意味着如果该方法对变量进行了变异,则该变异会丢失,因为它是在副本上执行的。这也是为什么可变值类型是不好的做法的另一个原因。很容易意外丢失突变!

这是您可以问的另一个问题:

1. 是浮点、双精度或十进制字面量的开头;接下来的东西应该是一个数字。那么为什么1.ToString() 不会导致错误呢?

这个问题的假设不正确。词法分析器检查点后面的东西是否是数字;如果是,那么它会继续将文字分析为浮点数、双精度数或十进制数。 (这将由后缀确定,如果有的话。)如果点后面的东西不是数字,那么词法分析器将点作为成员访问点进行词法分析,并开始为ToString 词法分析新标记。

【讨论】:

  • @EricLippert 我是 C# 的初学者,仍然需要了解许多概念。抱歉,下次我会尝试更好地解释我的问题。我不知道在 C# 中常量文字也被视为对象(System.Object)或更清楚地以前我认为常量文字作为仅存储在内存中的值和指向这些值的变量具有类型。所以在 var intStr = 1.ToString(); 的情况下我认为 1 只是 00000000 00000000 00000000 00000001 存储在内存中,而 intStr 是 int 类型的变量,并且具有以上二进制值作为其数据。
  • @EricLippert 你的回答没有错我从你的回答中学到了新东西,现在我知道在提问时该怎么想和做什么。 :) 谢谢
  • @SandeepKumar:你的想法是完全正确的!值类型的常量只是一种位模式,但C# 编译器知道该类型是 int。因此,当您调用 1.ToString() 时,编译器会说“创建一个 int 类型的临时变量,将 1 复制到该临时变量中,然后调用 Int32.ToString 并将该变量的引用设为 this。”
【解决方案2】:

它们是从常量字面量初始化的(在程序文本中),但它们实际上是对象(因为它们在逻辑上被视为对象),并且所有对象都实现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()

请注意,ldc.i4.1 是一条特殊指令,它将值为 1 的 System.Int32 压入堆栈。具体来说,就是实际“创建”System.Int32 值对象的指令。

还要注意,System.Int32虽然是值类型,但也被当作对象,所以下面的说法永远为真:

bool isObject = (1 is object);

【讨论】:

  • mm 小心...文字 1 是一个 int ,它在调用 ToString() 之前被装箱到一个对象。
  • 调用 int.ToString() 不会进行任何拳击 - 请参阅此答案:stackoverflow.com/questions/6423452/… 请注意,如果是拳击,IL 将包含 box 指令。它不对int 进行装箱,因为int 会覆盖ToString(),从而防止装箱发生。
  • @csj 马修是正确的。当值类型覆盖方法时,编译器会省略装箱。这就是为什么重写这些方法是个好主意的原因之一。
  • @MatthewWatson 您是否发布了错误的链接?那篇文章没有解释为什么在int 上调用ToString 不会装箱。它谈到了使用ToString 和拳击,但在不同的上下文中。
  • @CSJ:有趣的事实:1.GetType() 确实将 int 装箱。 GetType 不是虚拟的,因此不会在Int32 上被覆盖,因此this 必须是object 类型,所以它被装箱了。当然,首先你会很傻,因为typeof(int) 更清楚。
猜你喜欢
  • 2011-05-02
  • 2011-12-09
  • 1970-01-01
  • 1970-01-01
  • 2010-10-25
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 2012-10-16
相关资源
最近更新 更多