为什么c#中的字符串插值不适用于const字符串?例如:
private const string WEB_API_ROOT = "/private/WebApi/";
private const string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
从我的角度来看,一切都在编译时就知道了。或者这是一个稍后会添加的功能?
编译器消息:
分配给'DynamicWebApiBuilder.WEB_API_PROJECT'的表达式必须是常量。
非常感谢!
答案 0 :(得分:35)
插值字符串只是转换为对string.Format
的调用。所以你的上一行实际上是
private const string WEB_API_PROJECT = string.Format("{0}project.json", WEB_API_ROOT);
这不是编译时常量,因为包含了方法调用。
另一方面,字符串连接(简单的常量字符串文字)可以由编译器完成,因此这将起作用:
private const string WEB_API_ROOT = "/private/WebApi/";
private const string WEB_API_PROJECT = WEB_API_ROOT + "project.json";
或从const
切换到static readonly
:
private static readonly string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
所以在第一次访问声明类型的任何成员时,字符串被初始化(并且string.Format
被调用)。
答案 1 :(得分:15)
为什么字符串插值表达式不被视为常量的另一个解释是它们不是常数,即使它们的所有输入都是常量。具体而言,它们根据当前的文化而有所不同。尝试执行以下代码:
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
Console.WriteLine($"{3.14}");
CultureInfo.CurrentCulture = new CultureInfo("cs-CZ");
Console.WriteLine($"{3.14}");
它的输出是:
3.14
3,14
请注意,即使字符串插值表达式在两种情况下都相同,但输出也不同。因此,对于const string pi = $"{3.14}"
,它不清楚编译器应该生成什么代码。
答案 2 :(得分:5)
在roslyn 的Roslyn项目中进行了讨论,最终得出以下结论:
阅读摘录:
它不是一个错误,它明确地设计为像这样运作。你不喜欢它并不会成为一个错误。 串联字符串不需要String.Format,但事实并非如此 你在做什么。 你正在插入它们,String.Format是 需要基于规范和如何实现 插值在C#中工作。
如果要连接字符串,请转到 就在前面,使用与C#1.0相同的语法。 根据使用情况将实现更改为不同的行为 产生意想不到的结果:
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
甚至声明:
private static readonly string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
编译器引发错误:
"The name 'WEB_API_ROOT' does not exist in the current context".
变量' WEB_API_ROOT'应该在相同的上下文中定义
那么,对于OP的问题:为什么字符串插值不适用于const字符串? 答:这是C#6规范。有关详细信息,请参阅.NET Compiler Platform ("Roslyn") -String Interpolation for C#
答案 3 :(得分:2)
似乎 C# 10 将包含使用 const
内插字符串的能力,只要使用不涉及文化可能影响结果的场景(例如 this example)。换句话说,如果插值只是将字符串连接在一起,它会在编译时工作。
如果选择了 preview
语言版本,现在 VS 2019 版本 16.9 中允许这样做。
https://github.com/dotnet/csharplang/issues/2951#issuecomment-736722760
答案 4 :(得分:2)
在 C# 9.0 或更早版本中,我们不允许将 const
与内插字符串一起使用。
如果要将常量字符串合并在一起,则必须使用串联而不是插值。
const string WEB_API_ROOT = "/private/WebApi/";
const string WEB_API_PROJECT = WEB_API_ROOT + "project.json";
但从 C# 10.0 开始,允许 const
内插字符串作为 C# 语言的功能和增强功能。
C# 10.0 功能在 .NET 6.0 框架中可用,因为我们可以使用它。 见下面的代码,目前是 C# 10.0 (Preview 5)
const string WEB_API_ROOT = "/private/WebApi/";
const string WEB_API_PROJECT = $"{WEB_API_ROOT}project.json";
您还可以从官方网站 What's new in C# 10.0
签出文档答案 5 :(得分:0)
与string.Format
一起使用的常量,就其性质而言,旨在与特定数量的自变量一起使用,每个自变量具有预定的含义。
换句话说,如果您创建此常量:
const string FooFormat = "Foo named '{0}' was created on {1}.";
然后,要使用它,您必须有两个参数,它们可能应该是string
和DateTime
。
因此,即使在字符串插值之前,我们在某种意义上还是使用常数作为函数。换句话说,与其分隔常数,不如将其放入函数中可能更有意义,例如:
string FormatFooDescription(string fooName, DateTime createdDate) =>
string.Format("Foo named '{0}' was created on {1}.", fooName, createdDate);
这是一回事,除了常量(字符串文字)现在与使用它的函数和参数一起定位。它们也可能在一起,因为格式字符串对于任何其他目的都没有用。而且,现在您可以看到应用于格式字符串的参数的意图。
当我们这样看时,字符串插值的类似用法变得显而易见:
string FormatFooDescription(string fooName, DateTime createdDate) =>
$"Foo named '{fooName}' was created on {createdDate}.";
如果我们有多个格式字符串,并且希望在运行时选择一个特定的字符串,该怎么办?
我们可以选择一个函数来代替选择要使用的字符串:
delegate string FooDescriptionFunction(string fooName, DateTime createdDate);
然后我们可以这样声明实现:
static FooDescriptionFunction FormatFoo { get; } = (fooName, createdDate) =>
$"Foo named '{fooName}' was created on {createdDate}.";
或者,更好的是:
delegate string FooDescriptionFunction(Foo foo);
static FooDescriptionFunction FormatFoo { get; } = (foo) =>
$"Foo named '{foo.Name}' was created on {foo.CreatedDate}.";
}