簡體   English   中英

為什么插入 const 字符串會導致編譯器錯誤?

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

為什么 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”的表達式必須是常量。

非常感謝!

內插字符串被簡單地轉換為對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調用)。

字符串插值表達式不被視為常量的另一個解釋是它們不是 constant ,即使它們的所有輸入都是常量。 具體來說,它們因當前文化而異。 嘗試執行以下代碼:

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}" ,編譯器應該生成什么代碼就不清楚了。

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 編譯器平台(“Roslyn”)-C# 的字符串插值

似乎 C# 10 將包含使用const內插字符串的能力,只要該用法不涉及文化可能影響結果的場景(例如本示例)。 換句話說,如果插值只是將字符串連接在一起,它將在編譯時工作。

如果選擇了preview語言版本,現在在 VS 2019 版本 16.9 中允許這樣做。

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

在 C# 9.0 或更早版本中,我們不允許const插字符串使用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(預覽版 5)

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

您還可以從官方網站查看文檔C# 10.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}.";
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM