C# 在C中推断常数的类型#

C# 在C中推断常数的类型#,c#,constants,type-inference,C#,Constants,Type Inference,在C#中,以下类型推理起作用: var s = "abcd"; 但是,当变量是常量时,为什么不能推断类型呢 以下内容引发编译时异常: const var s = "abcd"; // <= Compile time error: // Implicitly-typed local variables cannot be constant const var s=“abcd”// IMO var的主要用途是允许匿名类型(类型未知,使用v

在C#中,以下类型推理起作用:

var s = "abcd";
但是,当变量是常量时,为什么不能推断类型呢

以下内容引发编译时异常:

const var s = "abcd"; // <= Compile time error: 
                      //    Implicitly-typed local variables cannot be constant

const var s=“abcd”// IMO var的主要用途是允许匿名类型(类型未知,使用var可以声明一个变量来存储它)。现在更常见的用法是编写更少的代码;)。

正如他们所解释的,如果你知道类型和值(不会改变),就写类型。

简短的回答是因为语言设计师(微软)这么说

发件人:

编译器错误CS0822

错误消息:隐式键入的局部变量 不能是常量

隐式类型的局部变量是 仅用于存储匿名文件 类型。在所有其他情况下,它们都是 只是为了方便。如果 变量永远不会改变,只需给出 它是一个显式类型。试图使用 带有 隐式类型的本地将生成 CS0106

要更正此错误

如果您要求变量为常量或只读,请给它一个 显式类型


有趣。我不知道这只是C#编译器的一个限制,还是语言本身的一个基本限制

为了解释我的意思,请考虑VB.

在VB9中,您也无法推断常量,但这只是编译器的一个限制。
在VB10中,他们能够添加常量类型推断,而无需对语言进行任何重大更改

在这种情况下,很明显,您知道引用类型将是常量,并且是相当原始的类型(常量只能是值类型或字符串等),因此您应该声明该类型,而不是使用隐式类型

换句话说,因为类型显然是常量和已知的,所以绝对没有理由使用var

隐式类型的局部变量是 仅用于存储匿名文件 类型。在所有其他情况下,它们都是 只是为了方便。如果 变量永远不会改变,只需给出 它是一个显式类型。试图使用 带有 隐式类型的本地将生成 CS0106

编译器错误CS0822

如果需要,请更正此错误 变量必须是常量或 只读,给它一个显式类型

我真的希望Lippert过来看看这个问题

如果你想引起我的注意,你可以在文本中留下我的名字——不是评论——我最终会找到的。或者,更好的是,你可以“推特”到
@ericlippert
。请注意,这并不构成服务级别协议;我在业余时间做这件事

当变量是常量时,为什么不能推断类型

“常量”和“变量”是对立的
const-var
让我打字时不寒而栗。常数是一个永不改变且没有存储位置的值;变量是内容发生变化的存储位置。它们完全不同,所以不要试图将它们结合起来。选择
var
语法来调用“这是一个变量”,我们坚持使用它

var
可以代表特定的类型声明,但将其与
const
结合使用会严重混淆编译器对值所做的操作。因此,不允许使用
const var
来防止这种混淆,您必须显式键入常量

我完全可以使用不使用
var
的推断常量:

const Pi = 3.14159;

我觉得很好。然而,我知道没有计划将其添加到C#。

这只是一个猜测,但我认为原因可能与编译时将常量值放入元数据(这本身具有微妙的后果)有关。我想知道编译器在如何将变量转换为元数据方面是否存在一些问题

在里克特通过C#的CLR中(第177页)

定义常量会导致创建 元数据。当代码引用 常量符号,编译器查找 元数据中的该符号 定义该常量的程序集, 提取常量的值,然后 将值嵌入到发出的IL中 代码

他接着指出,这意味着,由于这个原因,你不能得到一个常数对内存的引用。为了使这一点更加明确,在psuedo C#中,如果程序集a定义常量:

//Assembly A, Class Widget defines this:
public static const System.Decimal Pi = 3.14
然后您有一个消费者:

//somewhere in the Program.exe assembly
decimal myCircleCurcum = 2 * Widget.pi
program.exe的结果编译IL将执行类似以下伪代码的操作:

// pseudo-IL just to illustrate what would happen to the const
myCircleCurcum = 2*3.14
请注意,使用程序集根本不知道十进制3.14与程序集A有任何关系——它是为program.exe编写一个文本值。对我来说,这是C#编译器采取行动的一种合理方式——毕竟,程序集a明确声明了pi是一个常量(意味着该值是一次性的pi=3.14)。但是,我敢大胆猜测,99%的C#开发者不理解这一点的后果&可能会一时兴起将pi改为3.1415

常量的跨程序集版本故事非常糟糕(同样,这来自Richter),因为如果程序集a的常量发生更改(即重新编译),程序集a中包含常量的使用者将看不到更改。这可能会导致消费者很难找出程序集的bug。以至于我禁止我的团队使用常量。他们轻微的性能增益不值得他们可能导致的细微错误

只有当你知道值永远不会改变时,你才能真正使用常量。即使将某些值设置为常量,如pi,你也不能肯定你不希望自己的感知在将来改变

如果部件A定义:

decimal const pi = 3.14
然后您构建它,然后其他程序集使用它,如果您随后更改程序集A:

decimal const pi = 3.1415
和reb
const var ScaleFactor = 2500000000; // Type 'Int64'

...
int thisValue = getNextInt();
total += thisValue * ScaleFactor;
// This variable should never be overwritten!
readonly var target = 4;
class Program
{
    static void Main(string[] args)
    {
        foreach (var x in new[] { "asdf", })
        {
            System.Console.WriteLine(x);
            // error CS1656: Cannot assign to 'x' because it is a 'foreach iteration variable'
            x = "food";
        }
    }
}