简体   繁体   English

在C#中用类型推断一个常量

[英]Type-inferring a constant in C#

In C#, the following type-inference works: 在C#中,以下类型推断有效:

var s = "abcd";

But why can't the type be inferred when the variable is a constant? 但是,当变量是常数时,为什么不能推断出类型呢?

The following throws a compile-time exception: 以下引发了编译时异常:

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

I'm actually hoping Lippert pops by and and takes a look at the question 我实际上希望Lippert流行,然后看看这个问题

If there's something you want brought to my attention, you can leave my name in the text -- not a comment -- and I'll find it eventually. 如果您想引起我的注意,您可以在文本中留下我的名字 - 而不是评论 - 我最终会找到它。 Or, better, you can "tweet" to @ericlippert . 或者,更好的是,你可以“推特”到@ericlippert Note that this does not constitute a service level agreement; 请注意,这不构成服务级别协议; I do this in my spare time. 我在业余时间这样做。

why can't the type be inferred when the variable is a constant? 当变量是常量时,为什么不能推断出类型呢?

"constant" and "variable" are opposites . “常数”和“变量”是对立的 const var gives me the shudders to type. const var给了我打字的颤抖。 A constant is a value that never changes and has no storage location; 常量是一个永远不会改变且没有存储位置的值; a variable is a storage location whose contents change. 变量是其内容发生变化的存储位置。 They're completely different, so don't attempt to combine them. 它们完全不同,所以不要试图将它们结合起来。 The var syntax was chosen to call out "this is a variable", and we're sticking with it. 选择var语法来调用“这是一个变量”,我们坚持使用它。

var can stand in for a specific type declaration, but combining it with const severely muddies the picture of what the compiler does with the value. var可以站在一个特定类型声明,但相结合const严重muddies与价值的编译器什么的图片。 Therefore const var is disallowed to prevent this confusion and you have to explicitly type your constants. 因此,不允许使用const var来防止这种混淆,您必须显式键入常量。

I would be perfectly fine with inferred constants that do not use var : 对于不使用var推断常量,我会完全没问题:

const Pi = 3.14159;

seems fine to me. 对我来说似乎很好。 However, I know of no plans to add this to C#. 但是,我知道没有计划将其添加到C#中。

This is just a guess, but I think that the reason might have to do with the fact that const values are put in metadata (which has subtle consequences all it's own) when compiled. 这只是一个猜测,但我认为原因可能与const值在编译时放入元数据(它具有所有它自己的微妙后果)这一事实有关。 I wonder if maybe the compiler has some issues figuring out how to transform a var to metadata. 我想知道编译器是否有一些问题可以找出如何将var转换为元数据。

In Richter's CLR VIA C# (page 177), 在Richter的CLR VIA C#中 (第177页),

Defining a constant causes creation of metadata. 定义常量会导致元数据的创建。 When code refers to a constant symbol, compilers look up that symbol in the metadata of the assembly that defines that constant, extract the constant's value, and embed the value in the emitted IL code. 当代码引用常量符号时,编译器在定义该常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入到发出的IL代码中。

He goes on to note that this means that you can't get the reference to memory of a constant for this reason. 他接着指出,这意味着由于这个原因你无法获得对常量的记忆的引用。 To make this a bit more explicit, in psuedo C# if assembly A defines a const: 为了使它更加明确,在psuedo C#中,如果程序集A定义了一个const:

//Assembly A, Class Widget defines this:
public static const System.Decimal Pi = 3.14

then you have a consumer of A: 然后你有A的消费者:

//somewhere in the Program.exe assembly
decimal myCircleCurcum = 2 * Widget.pi

the resultant compiled IL of program.exe would do something like this pseudocode: 由此得到的program.exe编译后的IL会做类似这样的伪代码:

// pseudo-IL just to illustrate what would happen to the const
myCircleCurcum = 2*3.14

note that the consuming assembly has no idea that the decimal 3.14 had any relationship to Assembly A at all--it is to program.exe a literal value . 请注意, 消费组件根本不知道小数3.14与程序集A有任何关系 - 它是program.exe的一个文字值 This, to me, is a reasonable way for the C# compiler to act--after all, Assembly A declared explicitly that pi is a constant (meaning that the value is once and for all pi=3.14). 对我来说,这是C#编译器行为的合理方式 - 毕竟,程序集A 明确声明pi是一个常量(意味着值是一次且对于所有pi = 3.14)。 But, I'd venture to guess, that 99% of C# developers do not understand the ramifications of this & might change pi to be 3.1415 on a whim. 但是,我冒昧地猜测,99%的C#开发人员不理解这种情况的后果,可能会随心所欲地将pi改为3.1415。

Constants have a really poor cross-assembly version story (again, this comes from Richter) because a consumer of assembly A with a constant in it will not see a change if assembly A's constant changes (ie it was recompiled). 常量具有非常差的跨组件版本故事(同样,这来自Richter),因为如果程序集A的常量发生变化(即重新编译),组件A中具有常量的消费者将不会看到更改。 This can cause really hard to figure out bugs by consumer of assembly A. . 这可能导致很难找出组件A的消费者的错误。 . so much so that I ban my team from using constants. 所以我禁止我的团队使用常量。 Their slight perf gain is not worth the subtle bugs they can cause. 他们的轻微性能增加不值得他们可能导致的微妙错误。

You can really only ever use a constant if you know that the value will never, ever change -- and even with something set as a const such as pi, you can't say for sure that you won't want your percision to change in the future. 你真的只能使用一个常数,如果你知道这个值永远不会改变 - 甚至把某些东西设置成一个const就像pi一样,你不能肯定地说你不希望你的percision改变在将来。

if assembly A defines: 如果程序集A定义:

decimal const pi = 3.14

then you build it and then other assemblies consume it, if you then change assembly A: 然后你构建它,然后其他程序集使用它,如果你然后更改程序集A:

decimal const pi = 3.1415

and rebuild assembly A, the consumer of assembly A will still have the old value 3.14! 并重建程序集A,程序集A的使用者仍将具有旧值3.14! why? 为什么? because the original 3.14 was defined as a constant which means that the consumers of assembly A have been told that the value won't change--so they can bake that value of pi into their own metadata (if you rebuild consumer of assembly A it will then get the new value of pi in it's metadata). 因为原始3.14被定义为常量,这意味着程序集A的消费者被告知该值不会改变 - 因此他们可以将pi的值烘焙到他们自己的元数据中(如果你重建了程序集A的消费者)然后将在其元数据中获得pi的新值。 Again, I don't see this as a problem with the way CSC handles constants--it's just that developers probably don't expect that a constant can't be changed safely under some circumstances, where it can be changed safely in others. 同样,我不认为这是CSC处理常量的方式的问题 - 只是开发人员可能不希望在某些情况下不能安全地更改常量,在其他情况下可以安全地更改常量。 Safe: no consumers will ever have reference by .dll only (ie they will always build from source EVERY TIME), unsafe: consumers don't have a clue about when source code of your assembly with the const defined it it changes. 安全:没有消费者永远只能通过.dll引用(即它们将始终从源构建每个时间),不安全:消费者不知道你的程序集的源代码何时定义它改变了。 It probably should be made much more clear in .NET documentation that constant means you can't change the value in the sourcecode 在.NET文档中可能应该更加清楚, 常量意味着您无法更改源代码中的值

For that reason, I'd strongly suggest not using constants and instead just making the widget readonly. 出于这个原因,我强烈建议不要使用常量,而只是简单地使小部件。 How many values can you really say for certain are truly going to be const for ever and always? 你能真正说出多少值真的永远是永恒的?

The only real reason to use const over readonly in my mind is if something might have performance implications... but if you are running into that, I'd wonder if C# is really the correct language for your problem. 在我的脑海中使用const而不是readonly的唯一真正原因是,某些东西可能会对性能产生影响......但如果你遇到这种情况,我会想知道C#是否真的是你问题的正确语言。 In short, to me, it is alomst never a good idea to use constants. 简而言之,对我而言,使用常量绝不是一个好主意。 There are very few times where the tiny perf improvement is worth the potential problems. 极少数情况下,微小的性能改善值得潜在的问题。

I agree with Eric that this is ugly as sin: 我同意埃里克的观点,认为这是罪恶的丑陋:

const var s = "abcd"

But why not simply this? 但为什么不简单呢?

const s = "abcd"

Seems like a reasonable syntax to me. 对我来说似乎是一个合理的语法。

The short answer is because the language designers (Microsoft) say so. 简短的回答是因为语言设计师(微软)这么说。

From MSDN : 来自MSDN

Compiler Error CS0822 编译器错误CS0822

Error Message : Implicitly typed locals cannot be const 错误消息 :隐式键入的本地不能是const

Implicitly typed local variables are only necessary for storing anonymous types. 隐式类型的局部变量仅用于存储匿名类型。 In all other cases they are just a convenience. 在所有其他情况下,他们只是一个方便。 If the value of the variable never changes, just give it an explicit type. 如果变量的值永远不会改变,只需给它一个显式类型。 Attempting to use the readonly modifier with an implicitly typed local will generate CS0106. 尝试将readonly修饰符与隐式类型化的本地一起使用将生成CS0106。

To correct this error 纠正此错误

If you require the variable to be constant or readonly, give it an explicit type. 如果要求变量为常量或只读,请为其指定显式类型。

My answer? 我的答案? Since it is not currently possible to use "const var" don't even worry about it. 由于目前无法使用“const var”,所以甚至不用担心它。 That limitation, for no reason at all, makes C# unbalanced in how it treats constants versus variables and that creates an assymetry. 这种限制,完全没有任何理由,使得C#在处理常量与变量的方式上不平衡,从而产生了不对称性。 You'd be better off 你会好起来的

"The "var" syntax was chosen to call out "this is a variable", and we're sticking with it." “选择”var“语法来称呼”这是一个变量“,我们坚持使用它。”

I find Eric Lippert's arguemnt deeply unconvincing on multiple levels. 我发现Eric Lippert的争论在多个层面上都令人难以置信。

Eric, I don't know who "we" are and I really don't want to sound rude but both the use (as in the reason for being) AND meaning (as in why var is appropriate name) have nothing to do with the meaning you are trying to attach to it. 埃里克,我不知道“我们”是谁,我真的不想听起来很粗鲁,但是使用 (因为在存在的原因)和意义 (如为什么var是合适的名字)与无关你想要附加到它的意思。 "Var" is used it place of an an explicit type declaration and signifies the fact that it's type, at that point in time, can be one of many. “Var” 用于显式类型声明的位置,表示它在那个时间点的类型可以是众多类型之一。

To recap, var replaces the type declaration. 回顾一下,var替换了类型声明。 Let's not pretend that it does anything else because type and value (and whether or not this value can be changed) are two distinct things. 让我们不假装它做任何其他事情,因为类型和值(以及这个值是否可以改变)是两个截然不同的事情。 Occum's razor applies here and there is no need to expand the meaning of var beyond what it does. Occum的剃须刀在这里适用,没有必要扩展var的含义超出它的作用。

More importantly, even in the days when implicit declarations were not an option and the var keyword was in use people still thought of their objects as variables and had no problem declaring their variables as constants. 更重要的是,即使在隐式声明不是一个选项并且var关键字正在使用的日子里,人们仍然认为它们的对象是变量,并且没有问题将它们的变量声明为常量。

"var" was introduced because there was a need for it. 引入“var”是因为需要它。 And that need was not to make variables safe from becoming constants. 而这种需求并不是为了使变量不会成为常量。 That limited interpritation creates another need, that is currently not meet. 有限的interpritation创造了另一种需求,目前尚未满足。

Your whole stance can be deduced to a symantics argument - we simply don't like the way "const var sounds" (eg "gives me the shudders to type.") This is odd considering that one can type something like "dynamic static" without compilation errors and that sounds awkward too. 你的整个立场可以推导出一个语法参数 - 我们根本不喜欢“const var sounds”的方式(例如“给我颤抖的类型。”)这很奇怪,因为人们可以输入类似“动态静态”的东西没有编译错误,这听起来尴尬。

Sp why emphasize something that has absolutely no risk of being ambigious in the first place? Sp为什么强调一开始就没有任何风险的东西? Is "const var = "Hello World"" or some variation of thereof really going to make people puzzled weather it's a constant or not. 是“const var =”Hello World“”还是其中的一些变化真的会让人们感到困惑,天气是不变的。 I think people will be able to understand exactly what that means, just as they understand what "dynamic static" means. 我认为人们将能够准确理解这意味着什么,就像他们理解“动态静态”意味着什么一样。

The real bottom line is that being able to implicitly declare constants both makes sense and can actually be useful. 真正的底线是能够隐式声明常量既有意义又实际上有用。 There is currently no way to do that for seemingly no reason. 目前似乎没有理由,目前无法做到这一点。 And it makes a heck of a lot more sense to be able to declare "const var" than to introduce yet another keyword to serve implicitly declared constants. 能够声明“const var”比引入另一个关键字来提供隐式声明的常量更有意义。

And if you don't think that Eric's argument is entirely based on needlessly complex interpretation of semantics, try to build the same argument around the meaning of "var" if it's called by a different name. 如果你不认为Eric的论证完全是基于对语义的不必要的复杂解释,那么如果用不同的名称调用,则尝试围绕“var”的含义构建相同的参数。 Say, impl. 说,impl。 Would there be any reason why impl couldn't be used in conjunction with const? 是否有任何理由为什么impl不能与const一起使用? I'd be hard pressed to come up with a single reason for it. 我很难想出一个理由。 Therefore, it comes down to not liking the way "const var" sounds and nothing else. 因此,它归结为不喜欢“const var”声音的方式,而不是别的。 I think most of us could easily get over that. 我想我们大多数人都可以轻易克服这一点。

I don't agree with @Eric. 我不同意@Eric。

The var keyword doesn't mean "this is a variable", it means "the type is to be inferred". var关键字并不意味着“这是一个变量”,它意味着“要推断出类型”。

Did int, long, etc. are "keywords" to identify variables? int,long等是识别变量的“关键字”吗? No, they are just data types, can be used for variables or constants. 不,它们只是数据类型,可用于变量或常量。

I think the name of the keyword var was thought to resemble Javascript and I consider inappropriate. 我认为关键字var的名称被认为类似于Javascript,我认为不合适。

How about auto ? 汽车怎么样? ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1705.pdf ) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1705.pdf

While I disagree with Mr. Lippert's reasoning, there is a good reason not to allow implicit typing of named constants: consider the meaning of the following code, if typed constants did not have to specify their type explicitly: 虽然我不同意Lippert先生的推理,但有一个很好的理由不允许隐式输入命名常量:考虑下面代码的含义,如果类型常量不必明确指定它们的类型:

const var ScaleFactor = 2500000000; // Type 'Int64'

...
int thisValue = getNextInt();
total += thisValue * ScaleFactor;

Now suppose that the scale factor needs to be notched down by 20%. 现在假设比例因子需要减少20%。 What would be the effect of changing the value to 2000000000? 将值更改为2000000000会产生什么影响? While the problem of having an Int64 become an Int32 would occur even if the value were specifed in the code [eg when changing total += thisValue * 2500000000; 虽然在代码中指定了值,但是Int64成为Int32的问题会发生[例如,当改变total += thisValue * 2500000000; to total += thisValue * 2000000000; total += thisValue * 2000000000; the change would be adjacent to the code that requires that the value be an Int64 . 更改将与要求值为Int64的代码相邻。 By contrast, a const declaration would likely be far removed from the code it effects, so there would be no visible way of knowing whether code somewhere might rely upon a constant being a long type. 相比之下, const声明可能会远离它所影响的代码,因此没有可见的方法来了解某处的代码是否可能依赖于常量为long类型。

In this case it is obvious that you know the reference type will be constant, and of a fairly primitive type (consts can only be value types, or strings, etc..), so you should declare that type, rather than use implicit typing. 在这种情况下,很明显你知道引用类型是常量的,并且是一个相当原始的类型(consts只能是值类型,或字符串等等),所以你应该声明那个类型,而不是使用隐式类型。

In other words, because the type is obviously constant and known, there's absolutely no reason to use var. 换句话说,因为类型显然是常量且已知,所以绝对没有理由使用var。

Implicitly typed local variables are only necessary for storing anonymous types. 隐式类型的局部变量仅用于存储匿名类型。 In all other cases they are just a convenience. 在所有其他情况下,他们只是一个方便。 If the value of the variable never changes, just give it an explicit type. 如果变量的值永远不会改变,只需给它一个显式类型。 Attempting to use the readonly modifier with an implicitly typed local will generate CS0106. 尝试将readonly修饰符与隐式类型化的本地一起使用将生成CS0106。

http://msdn.microsoft.com/en-us/library/bb310881.aspx http://msdn.microsoft.com/en-us/library/bb310881.aspx

Compiler Error CS0822 编译器错误CS0822

To correct this error If you require the variable to be constant or readonly, give it an explicit type. 更正此错误如果要求变量为常量或只读,请为其指定显式类型。

Interesting. 有趣。 I don't know if it is just a limitation of the C# compiler or if it a fundemental limitaion of the language itself. 我不知道它是否只是C#编译器的限制,或者它是否是语言本身的有趣限制。

To explain what I mean, consider VB. 要解释我的意思,请考虑VB。

In VB 9 you also couldn't infer constants, but this was just a limitation of the compiler. 在VB 9中,您也无法推断常量,但这仅仅是编译器的限制。 In VB 10 they were able to add constant type inference without making any significant changes the to language. 在VB 10中,他们能够添加常量类型推断,而无需对语言进行任何重大更改。

IMO var main purpose is to allow anonymous types (type is unknown, with var you can declare a variable to store it). IMO var的主要目的是允许匿名类型(类型未知,使用var可以声明一个变量来存储它)。 The more common usage now is to write less code ;). 现在更常见的用法是编写更少的代码;)。 As they explain here if you know the type and the value (which won't change) just write the type. 正如他们在这里解释的那样如果您知道类型和值(不会改变),只需编写类型即可。

I realized that what I (we) actually wanted is the behavior of the const keyword as defined in JavaScript or C. That is, the ability to calculate it at runtime but disallow updating it in subsequent code. 我意识到我(我们)实际想要的是在JavaScript或C中定义的const关键字的行为。也就是说,它能够在运行时计算它,但不允许在后续代码中更新它。 This can be useful to force discipline and be explicit when you only want a value to be calculated once. 当你只想要计算一次值时,这对强制训练很有用并且是明确的。

In other words, what this question is really asking for is to be able to use the readonly keyword on locals (variables/method parameters). 换句话说,这个问题的真正要求是能够在locals上使用readonly关键字(变量/方法参数)。 Ie, this syntax might be useful: 即,这种语法可能有用:

// This variable should never be overwritten!
readonly var target = 4;

It is not like there is no precedent for this in C#. 这并不像C#中没有先例。 using() and iteration ( foreach ) variables already behave this way: using()和iteration( foreach )变量已经以这种方式运行:

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

Oh, look—I got type inference and readonly behavior! 哦,看 - 我得到了类型推断 readonly行为! Yay! 好极了! However, using the foreach keyword is way too clunky to actually do this in real code. 但是,使用foreach关键字实际上在实际代码中实际执行此操作太笨拙了。 It is not obvious at all that you're trying to protect yourself from, uh, yourself or your coworkers adding code that mutates x later without thinking through the implications (with the help of a compiler error). 根本没有明显的是,你试图保护自己,呃,你自己或你的同事添加的代码可以在不考虑其影响的情况下(在编译器错误的帮助下)改变x That is why it would be great if it became a language feature . 这就是为什么如果它成为语言功能会很棒的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM