简体   繁体   中英

Why does interpolating a const string result in a compiler error?

Why does string interpolation in c# does not work with const strings? 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.

Thanks a lot!

Interpolated strings are simply converted to calls to 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 :

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.

An additional explanation why string interpolation expressions are not considered constants is that they are not constant , even if all their inputs are constants. 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.

There is a discussion in Roslyn project at roslyn that finalize the following conclusion:

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. You're interpolating them, and String.Format is needed for that based on the spec and implementation of how interpolation works in C#.

If you want to concatenate strings, go right ahead and use the same syntax that has worked since 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

So, for the question of OP: Why does string interpolation is not working with const strings? Answer: It's by C# 6 specs. for more details read .NET Compiler Platform ("Roslyn") -String Interpolation for 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 ). 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.

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. 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 feature available in .NET 6.0 framework, in that we can able to use it. see below code, currently C# 10.0 (Preview 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

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.

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 .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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