简体   繁体   English

为什么插入 const 字符串会导致编译器错误?

[英]Why does interpolating a const string result in a compiler error?

Why does string interpolation in c# does not work with const strings?为什么 c# 中的字符串插值不适用于 const 字符串? For example:例如:

private const string WEB_API_ROOT = "/private/WebApi/";
private const string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";

From my point of view, everything is known at compile time.在我看来,一切都在编译时就知道了。 Or is that a feature that will be added later?或者这是一个稍后会添加的功能?

Compiler message:编译器消息:

The expression being assigned to 'DynamicWebApiBuilder.WEB_API_PROJECT' must be constant.分配给“DynamicWebApiBuilder.WEB_API_PROJECT”的表达式必须是常量。

Thanks a lot!非常感谢!

Interpolated strings are simply converted to calls to string.Format .内插字符串被简单地转换为对string.Format调用。 So your above line actually reads所以你上面的行实际上读

private const string WEB_API_PROJECT = string.Format("{0}project.json", WEB_API_ROOT);

And this is not compile time constant as a method call is included.这不是编译时常量,因为包含了方法调用。


On the other hand, string concatenation (of simple, constant string literals) can be done by the compiler, so this will work:另一方面,字符串连接(简单的常量字符串文字)可以由编译器完成,所以这将起作用:

private const string WEB_API_ROOT = "/private/WebApi/";
private const string WEB_API_PROJECT = WEB_API_ROOT + "project.json";

or switch from const to static readonly :或从const切换到static readonly

private static readonly string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";

so the string is initialized (and string.Format called) at the first access to any member of the declaring type.所以字符串在第一次访问声明类型的任何成员时被初始化(和string.Format调用)。

An additional explanation why string interpolation expressions are not considered constants is that they are not constant , even if all their inputs are constants.字符串插值表达式不被视为常量的另一个解释是它们不是 constant ,即使它们的所有输入都是常量。 Specifically, they vary based on the current culture.具体来说,它们因当前文化而异。 Try executing the following code:尝试执行以下代码:

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

Console.WriteLine($"{3.14}");

CultureInfo.CurrentCulture = new CultureInfo("cs-CZ");

Console.WriteLine($"{3.14}");

Its output is:它的输出是:

3.14
3,14

Note that the output is different, even though the string interpolation expression is the same in both cases.请注意,即使字符串插值表达式在两种情况下相同,输出也是不同的。 So, with const string pi = $"{3.14}" , it wouldn't be clear what code should the compiler generate.因此,对于const string pi = $"{3.14}" ,编译器应该生成什么代码就不清楚了。

There is a discussion in Roslyn project at roslyn that finalize the following conclusion:roslyn 的Roslyn 项目中有一个讨论,最终得出以下结论:

Read the excerpt:阅读摘录:

It's not a bug, it was explicitly designed to function like this.这不是一个错误,它被明确设计为这样的功能。 You not liking it doesn't make it a bug.你不喜欢它不会让它成为一个错误。 String.Format isn't needed for concatenating strings, but that's not what you're doing. String.Format 不需要连接字符串,但这不是你在做什么。 You're interpolating them, and String.Format is needed for that based on the spec and implementation of how interpolation works in C#.您正在对它们进行插值,并且需要 String.Format 根据插值在 C# 中的工作方式的规范和实现。

If you want to concatenate strings, go right ahead and use the same syntax that has worked since C# 1.0.如果您想连接字符串,请继续使用自 C# 1.0 以来一直有效的相同语法。 Changing the implementation to behave differently based on usage would produce unexpected results:根据使用情况将实现更改为不同的行为会产生意想不到的结果:

  const string FOO = "FOO";
  const string BAR = "BAR";
  string foobar = $"{FOO}{BAR}";
  const string FOOBAR = $"{FOO}{BAR}"; // illegal today

  Debug.Assert(foobar == FOOBAR); // might not always be true

Even the statement:甚至声明:

  private static readonly string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";

The compiler raise an error:编译器引发错误:

 "The name 'WEB_API_ROOT' does not exist in the current context". 

The variable 'WEB_API_ROOT' should be defined in the same context变量 'WEB_API_ROOT' 应该在相同的上下文中定义

So, for the question of OP: Why does string interpolation is not working with const strings?那么,对于 OP 的问题:为什么字符串插值不适用于 const 字符串? Answer: It's by C# 6 specs.答:它是由 C# 6 规范决定的。 for more details read .NET Compiler Platform ("Roslyn") -String Interpolation for C#有关更多详细信息,请阅读.NET 编译器平台(“Roslyn”)-C# 的字符串插值

It appears as though C# 10 will include the ability to use const interpolated strings, as long as the usage does not involve scenarios where culture may affect the outcome (such as this example ).似乎 C# 10 将包含使用const内插字符串的能力,只要该用法不涉及文化可能影响结果的场景(例如本示例)。 In other words, if the interpolation is simply concatenating strings together, it will work at compile time.换句话说,如果插值只是将字符串连接在一起,它将在编译时工作。

This is now allowed in VS 2019 version 16.9, if the preview language version is selected.如果选择了preview语言版本,现在在 VS 2019 版本 16.9 中允许这样做。

https://github.com/dotnet/csharplang/issues/2951#issuecomment-736722760 https://github.com/dotnet/csharplang/issues/2951#issuecomment-736722760

in C# 9.0 or earlier we can not allow to use const with interpolated strings.在 C# 9.0 或更早版本中,我们不允许const插字符串使用const if you want to merge constant strings together, you will have to use concatenation and not interpolation.如果要将常量字符串合并在一起,则必须使用串联而不是插值。

const string WEB_API_ROOT = "/private/WebApi/";
const string WEB_API_PROJECT = WEB_API_ROOT + "project.json";

but from C# 10.0 Allow const interpolated strings as features and enhancements to the C# language.但从 C# 10.0 开始,允许使用const内插字符串作为 C# 语言的功能和增强功能。

C# 10.0 feature available in .NET 6.0 framework, in that we can able to use it. C# 10.0 功能在 .NET 6.0 框架中可用,因为我们可以使用它。 see below code, currently C# 10.0 (Preview 5)请参阅下面的代码,当前为 C# 10.0(预览版 5)

const string WEB_API_ROOT = "/private/WebApi/";
const string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";

you can also checkout docs from official site What's new in C# 10.0您还可以从官方网站查看文档C# 10.0 的新增功能

A constant used with string.Format would, by its nature, be intended to work with a specific number of arguments which each have a predetermined meaning.string.Format一起使用的常量,就其性质而言,旨在处理特定数量的参数,每个参数都有预定的含义。

In other words, if you create this constant:换句话说,如果你创建这个常量:

const string FooFormat = "Foo named '{0}' was created on {1}.";

Then in order to use it you must have two arguments which are probably supposed to be a string and a DateTime .然后为了使用它,你必须有两个参数,它们可能应该是一个string和一个DateTime

So even before string interpolation we were in a sense using the constant as a function.因此,即使在字符串插值之前,我们在某种意义上也将常量用作函数。 In other words, instead of separating the constant, it might have made more sense to put it in a function instead, like this:换句话说,与其将常量分开,不如将其放在函数中更有意义,如下所示:

string FormatFooDescription(string fooName, DateTime createdDate) =>
    string.Format("Foo named '{0}' was created on {1}.", fooName, createdDate);

It's still the same thing, except that the constant (string literal) is now located with the function and arguments that use it.它仍然是一样的东西,除了常量(字符串文字)现在与使用它的函数和参数一起定位。 They might as well be together, because the format string is useless for any other purpose.它们也可能在一起,因为格式字符串对于任何其他目的都是无用的。 What's more, now you can see the intent of the arguments that are applied to the format string.更重要的是,现在您可以看到应用于格式字符串的参数的意图。

When we look at it that way, the similar use of string interpolation becomes obvious:当我们这样看时,字符串插值的类似用法就变得很明显了:

string FormatFooDescription(string fooName, DateTime createdDate) =>
    $"Foo named '{fooName}' was created on {createdDate}.";

What if we have multiple format strings and we want to choose a particular one at runtime?如果我们有多个格式字符串并且我们想在运行时选择一个特定的字符串怎么办?

Instead of selecting which string to use, we could select a function:我们可以选择一个函数,而不是选择要使用的字符串:

delegate string FooDescriptionFunction(string fooName, DateTime createdDate);

Then we could declare implementations like this:然后我们可以像这样声明实现:

static FooDescriptionFunction FormatFoo { get; } = (fooName, createdDate) => 
    $"Foo named '{fooName}' was created on {createdDate}.";

Or, better yet:或者,更好的是:

delegate string FooDescriptionFunction(Foo foo);

static FooDescriptionFunction FormatFoo { get; } = (foo) => 
    $"Foo named '{foo.Name}' was created on {foo.CreatedDate}.";
}

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

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