本檔列出在 .NET 6 一般版本 (.NET SDK 6.0.100 版) 到 .NET 7 一般版本 (.NET SDK 7.0.100 版) 之後,Roslyn 中已知的重大變更。
所有受限類型的局部變數在異步方法中都是不允許的。
Visual Studio 2022 17.6p1 版中引進
異步方法中不允許使用受限制類型的局部變數。 但在舊版中,編譯程式無法注意到一些隱含宣告的局部變數。 例如,在 foreach 或 using 中的語句或解構。
現在,也不允許隱式定義的區域變數。
ref struct RefStruct { public void Dispose() { } }
public class C
{
public async Task M()
{
RefStruct local = default; // disallowed
using (default(RefStruct)) { } // now disallowed too ("error CS9104: A using statement resource of this type cannot be used in async methods or async lambda expressions")
}
}
請參閱 https://github.com/dotnet/roslyn/pull/66264
指標必須一律處於不安全的情境中。
Visual Studio 2022 17.6 版中引進
在先前的 SDK 中,編譯程式偶爾會允許參考指標的位置,而不會明確將該位置標示為不安全。
現在, unsafe 修飾詞必須存在。
例如 using Alias = List<int*[]>; ,應該變更為 using unsafe Alias = List<int*[]>; 合法。
之類的 void Method(Alias a) ... 用法應該變更為 unsafe void Method(Alias a) ...。
規則是無條件的,但using別名宣告是例外(在 C# 12 之前不允許unsafe修飾詞)。
因此,針對 using 宣告,只有在選擇語言版本為 C# 12 或更高版本時,規則才會生效。
System.TypedReference 被視為受控管理
Visual Studio 2022 17.6 版中引進
至於未來,System.TypedReference 類型將被視為受管理的。
unsafe
{
TypedReference* r = null; // warning: This takes the address of, gets the size of, or declares a pointer to a managed type
var a = stackalloc TypedReference[1]; // error: Cannot take the address of, get the size of, or declare a pointer to a managed type
}
Ref 安全錯誤不會影響從 Lambda 運算式轉換成委派
Visual Studio 2022 17.5 版中引進
Lambda 主體中回報的 Ref 安全錯誤不再影響 Lambda 運算式是否可轉換為委派類型。 這項變更可能會影響過載解析。
在下面的範例中,對 M(x => ...) 的呼叫,在 Visual Studio 17.5 中存在模棱兩可的情況,因為 M(D1) 和 M(D2) 現在都被視為適用。即使是在 Lambda 主體中對 F(ref x, ref y) 的呼叫會導致 ref 安全性問題,與 M(D1) 有關(請參閱 d1 和 d2 中的範例以進行比較)。 先前,呼叫明確系結至 M(D2) ,因為 M(D1) 多載被視為不適用。
using System;
ref struct R { }
delegate R D1(R r);
delegate object D2(object o);
class Program
{
static void M(D1 d1) { }
static void M(D2 d2) { }
static void F(ref R x, ref Span<int> y) { }
static void F(ref object x, ref Span<int> y) { }
static void Main()
{
// error CS0121: ambiguous between: 'M(D1)' and 'M(D2)'
M(x =>
{
Span<int> y = stackalloc int[1];
F(ref x, ref y);
return x;
});
D1 d1 = x1 =>
{
Span<int> y1 = stackalloc int[1];
F(ref x1, ref y1); // error CS8352: 'y2' may expose referenced variables
return x1;
};
D2 d2 = x2 =>
{
Span<int> y2 = stackalloc int[1];
F(ref x2, ref y2); // ok: F(ref object x, ref Span<int> y)
return x2;
};
}
}
若要因應多載解析變更,請使用 Lambda 參數或委派的明確類型。
// ok: M(D2)
M((object x) =>
{
Span<int> y = stackalloc int[1];
F(ref x, ref y); // ok: F(ref object x, ref Span<int> y)
return x;
});
開頭行的原始字串插值。
Visual Studio 2022 17.5 版中引進
在 .NET SDK 7.0.100 或之前版本中,錯誤地允許以下內容:
var x = $"""
Hello
{1 + 1}
World
""";
這違反了行內容(包括插補開始的位置)必須以與最後 """; 一行相同的空格符開頭的規則。 現在需要將上述撰寫為:
var x = $"""
Hello
{1 + 1}
World
""";
方法的推斷委派類型包括預設參數值和 params 修飾詞
Visual Studio 2022 17.5 版中引進
在 .NET SDK 7.0.100 或更早版本中,從方法推斷的委派類型會忽略預設參數值和 params 修飾詞,如下列程式代碼所示:
void Method(int i = 0, params int[] xs) { }
var action = Method; // System.Action<int, int[]>
DoAction(action, 1); // ok
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });
在 .NET SDK 7.0.200 或更新版本中,這類方法會推斷為具有相同預設參數值和 params 修飾詞的匿名合成委派類型。
這項變更可能會中斷上述程序代碼,如下所示:
void Method(int i = 0, params int[] xs) { }
var action = Method; // delegate void <anonymous delegate>(int arg1 = 0, params int[] arg2)
DoAction(action, 1); // error CS1503: Argument 1: cannot convert from '<anonymous delegate>' to 'System.Action<int, int[]>'
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });
您可以在相關聯的 提案中深入瞭解這項變更。
為了進行明確的指派分析,異步區域函式的調用不再被視為等候
Visual Studio 2022 17.5 版中引進
為了進行明確指派分析,非同步區域函式的調用不再視為正在等待,因此,該區域函式不會被視為完全執行。 如需理由,請參閱 https://github.com/dotnet/roslyn/issues/43697 。
下列程式代碼現在會回報明確的指派錯誤:
public async Task M()
{
bool a;
await M1();
Console.WriteLine(a); // error CS0165: Use of unassigned local variable 'a'
async Task M1()
{
if ("" == String.Empty)
{
throw new Exception();
}
else
{
a = true;
}
}
}
INoneOperation 屬性的節點現在是 IAttributeOperation 節點。
Visual Studio 2022 17.5 版、.NET SDK 7.0.200 版引進
在舊版的編譯器中,IOperation 屬性的樹狀結構是以 INoneOperation 節點作為根。
我們已新增屬性的原生支持,這表示樹的根節點現在是 IAttributeOperation。 某些分析器,包括舊版的 .NET SDK 分析器,不會預期這種樹狀結構,並且在遇到這種結構時會錯誤地警告(或者可能無法警告)。 此專案的因應措施如下:
- 可能的話,請更新分析器版本。 如果使用 .NET SDK 或舊版的 Microsoft.CodeAnalysis.FxCopAnalyzers,請更新為 Microsoft.CodeAnalysis.NetAnalyzers 7.0.0-preview1.22464.1 或更新版本。
- 暫時抑制分析器中的任何誤判,直到可以更新至包含此更改的版本。
不支援 ref 結構體的類型測試。
Visual Studio 2022 17.4 版中引進
使用ref struct類型於 'is' 或 'as' 運算符時,在某些情況下,編譯器先前曾錯誤地發出警告,指出類型檢查在運行時總是失敗,忽略了實際的類型檢查,導致行為不正確。 當執行時可能出現不正確行為,編譯器現在會報錯。
ref struct G<T>
{
public void Test()
{
if (this is G<int>) // Will now produce an error, used to be treated as always `false`.
{
來自 ref local 的未使用結果會被解除引用。
Visual Studio 2022 17.4 版中引進
當局部變數透過值進行參考時,但結果卻未被使用(例如指派給捨棄符號),之前該結果會被忽略。 編譯程式現在會解引用此本地變數,確保任何副作用都被觀察到。
ref int local = Unsafe.NullRef<int>();
_ = local; // Will now produce a `NullReferenceException`
無法命名類型 scoped
Visual Studio 2022 17.4 版中引進。 從 C# 11 開始,類型不能命名 scoped為 。 編譯程式會報告所有這類類型名稱的錯誤。 若要解決此問題,必須使用 @ 轉義類型名稱以及所有用法:
class scoped {} // Error CS9056
class @scoped {} // No error
ref scoped local; // Error
ref scoped.nested local; // Error
ref @scoped local2; // No error
現在, scoped 這是變數宣告的修飾詞,並在 ref 類型中保留下列內容 ref 。
無法命名類型 file
Visual Studio 2022 17.4 版中引進。 從 C# 11 開始,類型不能命名 file為 。 編譯程式會報告所有這類類型名稱的錯誤。 若要解決此問題,必須使用 @ 轉義類型名稱以及所有用法:
class file {} // Error CS9056
class @file {} // No error
這是因為 file 現在作為型別宣告的修飾詞。
您可以在相關聯的 csharplang 問題中深入瞭解這項變更。
#line span 指示詞中的必要空格
引入於 .NET SDK 6.0.400 和 Visual Studio 2022 版本 17.3。
在 C# 10 中引入的 span 指令不需要特定的空格。
例如,這會是有效的: #line(1,2)-(3,4)5"file.cs"。
在 Visual Studio 17.3 中,編譯程式在第一個括號、字元位移和檔名之前需要空格。
因此,除非新增空格,否則上述範例無法剖析: #line (1,2)-(3,4) 5 "file.cs"。
System.IntPtr 和 System.UIntPtr 上的檢查過的運算子
在 .NET SDK 7.0.100 中引進,Visual Studio 2022 17.3 版。
當平臺支持 數值IntPtr 和 UIntPtr 類型(如 System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr 所示)時,來自 nint 和 nuint 的內建運算子會套用至這些基礎類型。
這表示在這類平臺上, IntPtr 而且 UIntPtr 具有內 checked 建運算符,現在可以在發生溢位時擲回。
IntPtr M(IntPtr x, int y)
{
checked
{
return x + y; // may now throw
}
}
unsafe IntPtr M2(void* ptr)
{
return checked((IntPtr)ptr); // may now throw
}
可能的因應措施包括:
- 指定
unchecked上下文 - 降級至沒有數字型別
IntPtr/UIntPtr的平台/TFM
此外,與其他數值型別之間的 IntPtr/UIntPtr 隱含轉換會被視為這類平臺上的標準轉換。 在某些情況下,這可能會影響多載解析。
如果使用者程式代碼相依於未核取內容中的溢位例外狀況,或未預期檢查內容中的溢位例外狀況,則這些變更可能會導致行為變更。 已在 7.0 中新增分析器,以協助偵測這類行為變更並採取適當的動作。 分析器會產生潛在行為變更的診斷,其預設為資訊嚴重性,但可透過 editorconfig升級至警告。
新增 System.UIntPtr 和 System.Int32
在 .NET SDK 7.0.100 中引進,Visual Studio 2022 17.3 版。
當平臺支持數值IntPtr和UIntPtr型別時(如 是否存在System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr所示),就無法再使用 中+(UIntPtr, int)定義的運算符System.UIntPtr。
相反地,將類型 System.UIntPtr 的運算式和 System.Int32 新增後會導致錯誤:
UIntPtr M(UIntPtr x, int y)
{
return x + y; // error: Operator '+' is ambiguous on operands of type 'nuint' and 'int'
}
可能的因應措施包括:
- 使用
UIntPtr.Add(UIntPtr, int)方法:UIntPtr.Add(x, y) - 將未核取的轉換套用至第二個作數上的類型
nuint:x + unchecked((nuint)y)
方法或本機函式上屬性中的 Nameof 運算符
引入於 .NET SDK 6.0.400 和 Visual Studio 2022 版本 17.3。
當語言版本為 C# 11 或更新版本時, nameof 方法上的 屬性中的運算元會在範圍中引進該方法的類型參數。 這同樣適用於本機函式。
nameof方法上屬性中的運算符,其型別參數或參數會將該方法的參數帶入範圍中。 這同樣適用於本機函式、lambda 表達式、委託和索引器。
例如,現在這些會是錯誤:
class C
{
class TParameter
{
internal const string Constant = """";
}
[MyAttribute(nameof(TParameter.Constant))]
void M<TParameter>() { }
}
class C
{
class parameter
{
internal const string Constant = """";
}
[MyAttribute(nameof(parameter.Constant))]
void M(int parameter) { }
}
可能的因應措施包括:
- 重新命名類型參數或參數,以避免名稱被外部範圍覆蓋。
- 請使用字串常值,而非
nameof運算符。
無法以參考方式傳回 out 參數
在 .NET SDK 7.0.100 中引進,Visual Studio 2022 17.3 版。
在使用 C# 11 或更新版本,或 .NET 7.0 或更新版本時,參數 out 無法以傳址方式傳回。
static ref T ReturnOutParamByRef<T>(out T t)
{
t = default;
return ref t; // error CS8166: Cannot return a parameter by reference 't' because it is not a ref parameter
}
可能的因應措施包括:
使用
System.Diagnostics.CodeAnalysis.UnscopedRefAttribute將參考標示為未限定範圍。static ref T ReturnOutParamByRef<T>([UnscopedRef] out T t) { t = default; return ref t; // ok }變更方法簽章,以透過
ref傳遞 參數。static ref T ReturnRefParamByRef<T>(ref T t) { t = default; return ref t; // ok }
ref 結構上的實例方法可能會擷取未限定範圍的 ref 參數
在 .NET SDK 7.0.100 中和 Visual Studio 2022 版本 17.4 中引入。
使用語言版本 C# 11 或更新版本,或使用 .NET 7.0 或更新版本, ref struct 假設實例方法調用會擷取未限定範圍 ref 或 in 參數。
R<int> Use(R<int> r)
{
int i = 42;
r.MayCaptureArg(ref i); // error CS8350: may expose variables referenced by parameter 't' outside of their declaration scope
return r;
}
ref struct R<T>
{
public void MayCaptureArg(ref T t) { }
}
如果在ref實例方法中未擷取in或ref struct參數,則可能的因應措施是將參數宣告為scoped ref或scoped in。
R<int> Use(R<int> r)
{
int i = 42;
r.CannotCaptureArg(ref i); // ok
return r;
}
ref struct R<T>
{
public void CannotCaptureArg(scoped ref T t) { }
}
方法 ref 結構的傳回逸出分析取決於 ref 参数的逸出分析
在 .NET SDK 7.0.100 中和 Visual Studio 2022 版本 17.4 中引入。
使用 C# 11 或更新版本的語言版本,或 .NET 7.0 或更新版本,從方法調用中返回的 ref struct,無論是作為返回值還是作為out參數,只有在方法調用的所有和ref引數皆為in時,才被視為安全逸出。
自 in 變數可能包含隱含的預設參數值。
ref struct R { }
static R MayCaptureArg(ref int i) => new R();
static R MayCaptureDefaultArg(in int i = 0) => new R();
static R Create()
{
int i = 0;
// error CS8347: Cannot use a result of 'MayCaptureArg(ref int)' because it may expose
// variables referenced by parameter 'i' outside of their declaration scope
return MayCaptureArg(ref i);
}
static R CreateDefault()
{
// error CS8347: Cannot use a result of 'MayCaptureDefaultArg(in int)' because it may expose
// variables referenced by parameter 'i' outside of their declaration scope
return MayCaptureDefaultArg();
}
如果ref或in參數未在ref struct傳回值中擷取,可能的解決方案是將參數宣告為scoped ref或scoped in。
static R CannotCaptureArg(scoped ref int i) => new R();
static R Create()
{
int i = 0;
return CannotCaptureArg(ref i); // ok
}
ref to ref struct 自變數視為無範圍 __arglist
在 .NET SDK 7.0.100 中和 Visual Studio 2022 版本 17.4 中引入。
使用語言版本 C# 11 或更新版本,或使用 .NET 7.0 或更新版本時,當 ref 類型轉換為 ref struct 時,若做為引數傳遞至 __arglist,會被視為未限定範圍的參考。
ref struct R { }
class Program
{
static void MayCaptureRef(__arglist) { }
static void Main()
{
var r = new R();
MayCaptureRef(__arglist(ref r)); // error: may expose variables outside of their declaration scope
}
}
無符號右移運算子
.NET SDK 6.0.400 和 Visual Studio 2022 版本 17.3 中引入了對「無符號右移」運算子的支援。 這會停用將實作使用者定義「無符號右移」運算符的方法作為一般方法來使用的能力。
例如,有一個以某些語言(VB 或 C# 以外)開發的現有程式庫,它公開類型 C1 的「無符號右移」使用者自訂運算子。
下列程式碼之前都成功編譯:
static C1 Test1(C1 x, int y) => C1.op_UnsignedRightShift(x, y); //error CS0571: 'C1.operator >>>(C1, int)': cannot explicitly call operator or accessor
可能的因應措施是切換至使用 >>> 運算符:
static C1 Test1(C1 x, int y) => x >>> y;
Foreach 列舉器作為 ref 結構體
在 .NET SDK 6.0.300 中引進,Visual Studio 2022 17.2 版。foreach使用 ref 結構列舉值型別的 ,如果語言版本設定為 7.3 或更早版本,就會報告錯誤。
這修正了一個錯誤,即功能在較新的編譯器上得到支援,但目標卻是尚未支援該功能的較早 C# 版本。
可能的因應措施包括:
- 將
ref struct類型變更為struct或class類型。 - 將
<LangVersion>項目升級至 7.3 或更新版本。
Async foreach 偏好以模式為基礎的 DisposeAsync,而不是 IAsyncDisposable.DisposeAsync() 的明確介面實作
在 .NET SDK 6.0.300 中引進,Visual Studio 2022 17.2 版。 異步 foreach 偏好使用模式型 DisposeAsync() 方法系結,而不是 IAsyncDisposable.DisposeAsync()。
例如,會選擇 DisposeAsync() ,而不是 IAsyncEnumerator<int>.DisposeAsync() 的 AsyncEnumerator 方法:
await foreach (var i in new AsyncEnumerable())
{
}
struct AsyncEnumerable
{
public AsyncEnumerator GetAsyncEnumerator() => new AsyncEnumerator();
}
struct AsyncEnumerator : IAsyncDisposable
{
public int Current => 0;
public async ValueTask<bool> MoveNextAsync()
{
await Task.Yield();
return false;
}
public async ValueTask DisposeAsync()
{
Console.WriteLine("PICKED");
await Task.Yield();
}
ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
}
這項變更修正了在宣告類型上可見的公用 DisposeAsync 方法的規格違規,而只有透過介面類型的引用才能看到明確的介面實作。
若要因應此錯誤,請從您的類型中移除模式型 DisposeAsync 方法。
不允許將轉換的字串當做預設自變數
在 .NET SDK 6.0.300 中引進,Visual Studio 2022 17.2 版。 C# 編譯程式會接受不正確的預設自變數值,涉及字串常數的參考轉換,並且會發出 null 作為常數值,而不是來源中指定的預設值。 在 Visual Studio 17.2 中,這會變成錯誤。 請參閱 roslyn#59806。
這項變更會修正編譯程式中的規格違規。 默認自變數必須是編譯時間常數。 舊版允許下列程式代碼:
void M(IEnumerable<char> s = "hello")
此宣告需要從 string 轉換為 IEnumerable<char>。 編譯程式允許此建構,並將發出 null 作為自變數的值。 上述程式代碼會在 17.2 開始產生編譯程式錯誤。
若要解決此問題,您可以進行下列其中一項變更:
- 變更參數類型,因此不需要轉換。
- 將預設自變數的值變更為
null,以還原先前的行為。
內容關鍵詞 var 做為明確的 Lambda 傳回型別
在 .NET SDK 6.0.200 中引進,Visual Studio 2022 17.1 版。 內容關鍵詞 var 不能當做明確的 Lambda 傳回型別使用。
透過確保成為lambda表達式回傳型別的自然類型,這項變更能啟用var。
如果您有一個名為 var 的類型,並使用明確的回傳類型 var 來定義 Lambda 運算式,那麼您可能會遇到此錯誤。
using System;
F(var () => default); // error CS8975: The contextual keyword 'var' cannot be used as an explicit lambda return type
F(@var () => default); // ok
F(() => default); // ok: return type is inferred from the parameter to F()
static void F(Func<var> f) { }
public class var
{
}
因應措施包括下列變更:
- 使用
@var做為傳回型別。 - 拿掉明確的傳回型別,讓編譯程式判斷傳回型別。
插入字串處理程式和索引器初始化
在 .NET SDK 6.0.200 中引進,Visual Studio 2022 17.1 版。 接受插入字串處理程式且需要接收者做為建構函式輸入的索引器,不能用於物件初始化表達式中。
這項變更不允許邊緣案例,其中索引器初始化表達式使用插補字串處理程式,而插補字串處理程式會將索引器接收者當做建構函式的參數。 此變更的原因是此案例可能會導致存取尚未初始化的變數。 請考慮下列範例:
using System.Runtime.CompilerServices;
// error: Interpolated string handler conversions that reference
// the instance being indexed cannot be used in indexer member initializers.
var c = new C { [$""] = 1 };
class C
{
public int this[[InterpolatedStringHandlerArgument("")] CustomHandler c]
{
get => ...;
set => ...;
}
}
[InterpolatedStringHandler]
class CustomHandler
{
// The constructor of the string handler takes a "C" instance:
public CustomHandler(int literalLength, int formattedCount, C c) {}
}
因應措施包括下列變更:
- 從插入字串處理程式中移除接收者類型。
- 將索引器的引數變更為
string
ref、readonly ref、in、out 不允許用於僅由 Unmanaged 呼叫的方法的參數或回傳值。
在 .NET SDK 6.0.200 中引進,Visual Studio 2022 17.1 版。ref/ref readonly/in/out 不允許在 以 UnmanagedCallersOnly屬性化之方法的傳回/參數上使用。
這項變更是錯誤修正。 傳回值和參數不是 blittable。 以參考傳遞參數或傳回值可能會導致未定義的行為。 這些宣告全部都無法編譯:
using System.Runtime.InteropServices;
[UnmanagedCallersOnly]
static ref int M1() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.
[UnmanagedCallersOnly]
static ref readonly int M2() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.
[UnmanagedCallersOnly]
static void M3(ref int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.
[UnmanagedCallersOnly]
static void M4(in int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.
[UnmanagedCallersOnly]
static void M5(out int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.
因應措施是移除「by reference」修飾詞。
長度和計數,被假設在模式中為非負數
在 .NET SDK 6.0.200 和 Visual Studio 2022 版 17.1 中引入。Length 在可計數和可索引型別上的 Count 屬性被假定為非負,以進行模式和交換機的次補及詳盡性分析。
這些類型可以搭配隱含的索引器與清單模式使用。
Length和 Count 屬性,即使類型為 int,在分析模式時會假設為非負數。 請考慮此範例方法:
string SampleSizeMessage<T>(IList<T> samples)
{
return samples switch
{
// This switch arm prevents a warning before 17.1, but will never happen in practice.
// Starting with 17.1, this switch arm produces a compiler error.
// Removing it won't introduce a warning.
{ Count: < 0 } => throw new InvalidOperationException(),
{ Count: 0 } => "Empty collection",
{ Count: < 5 } => "Too small",
{ Count: < 20 } => "reasonable for the first pass",
{ Count: < 100 } => "reasonable",
{ Count: >= 100 } => "fine",
};
}
void M(int[] i)
{
if (i is { Length: -1 }) {} // error: impossible under assumption of non-negative length
}
在17.1之前,測試Count是否為負值的第一個選擇臂是必要的,以避免警告所有可能的值並未涵蓋。 從 17.1 開始,第一個 switch arm 會產生編譯程序錯誤。 因應措施是移除針對無效案例新增的開關臂。
這項變更是在新增清單模式的一部分進行。 如果將集合中每一次使用 Length 或 Count 屬性都視為非負數,則處理規則會更加一致。 您可以在 語言設計問題中深入瞭解變更的詳細數據。
因應措施是移除具有無法達成條件的開關臂。
在結構中新增字段初始化器需要明確宣告的建構函式
在 .NET SDK 6.0.200 中引進,Visual Studio 2022 17.1 版。struct 具有欄位初始化表達式的類型宣告必須包含明確宣告的建構函式。 此外,所有欄位都必須在沒有struct初始化運算式的實例建構函式: this()中明確指派,因此必須從新增的建構函式或欄位初始化運算式指派任何先前未指派的欄位。 請參閱 dotnet/csharplang#5552、 dotnet/roslyn#58581。
有兩種方式可將變數初始化為 C# 中的預設值: new() 和 default。 對於類別而言,差異很明顯,因為 new 會建立新的 實體並 default 傳 null回 。 結構的差異比較微妙,因為 對於 default,結構會傳回實例,每個字段/屬性都設定為自己的預設值。 我們已在 C# 10 中新增結構欄位初始化運算式。 只有在明確宣告的建構函式執行時,才會執行字段初始化表達式。 值得注意的是,當您使用 default 或建立任何類型的 struct 陣列時,它們不會執行。
在 17.0 中,如果有字段初始化運算式,但沒有宣告的建構函式,則會合成執行字段初始化表達式的無參數建構函式。 不過,這表示新增或移除建構子宣告可能會影響是否合成無參數建構子,因此可能會變更 new() 的行為。
為了解決此問題,在 .NET SDK 6.0.200 (VS 17.1) 中,編譯程式不再合成無參數建構函式。
struct如果 包含欄位初始化運算式且沒有明確的建構函式,編譯程式會產生錯誤。
struct如果 具有欄位初始化運算式,則必須宣告建構函式,因為否則永遠不會執行欄位初始化運算式。
此外,除非建構函式具有初始化器,否則在每個 struct 建構函式中都必須指派所有沒有欄位初始化器的欄位。
例如:
struct S // error CS8983: A 'struct' with field initializers must include an explicitly declared constructor.
{
int X = 1;
int Y;
}
因應措施是宣告建構函式。 除非欄位先前未指派,否則此建構函式通常可以是空的無參數建構函式:
struct S
{
int X = 1;
int Y;
public S() { Y = 0; } // ok
}
格式規範不能包含大括號
在 .NET SDK 6.0.200 中引進,Visual Studio 2022 17.1 版。 插入字串中的格式規範不能包含大括弧(或 {})。 在先前版本中,{{ 被解釋為格式規範中的跳脫 {,而 }} 被解釋為跳脫 } 字元。 現在格式規範中的第一 } 個字元會結束插補,而且任何 { 字元都是錯誤。
這使插補字串的處理與 System.String.Format 的處理一致。
using System;
Console.WriteLine($"{{{12:X}}}");
//prints now: "{C}" - not "{X}}"
X 是大寫十六進位的格式,而 C 是12的十六進位值。
因應措施是移除格式字串中的額外大括弧。
您可以在相關聯的 roslyn 問題中深入瞭解這項變更。
無法命名類型 required
Visual Studio 2022 17.3 版中引進。 從 C# 11 開始,類型不能命名 required為 。 編譯程式會報告所有這類類型名稱的錯誤。 若要解決此問題,必須使用 @ 轉義類型名稱以及所有用法:
class required {} // Error CS9029
class @required {} // No error
現在 required 作為屬性和欄位的成員修飾詞。
您可以在相關聯的 csharplang 問題中深入瞭解這項變更。