本檔列出在 .NET 7 一般版本 (.NET SDK 7.0.100 版) 到 .NET 8 一般版本 (.NET SDK 8.0.100 版) 之後的 Roslyn 已知重大變更。
動態自變數的 Ref 修飾詞應該與對應參數的 ref 修飾詞相容
Visual Studio 2022 17.10 版中引進
動態自變數的 Ref 修飾詞應該與編譯時期對應參數的 ref 修飾詞相容。 這可能會導致涉及動態參數的多載解析在編譯階段失敗,而不是在執行階段失敗。
之前在編譯階段允許多重載入解析的不相符,並將此解析錯誤延遲至運行時間發生。
例如,下列程式代碼用來編譯時沒有發生錯誤,但發生例外狀況失敗:「Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:『C.f(ref object)』 的最佳多載方法比對有一些無效的自變數」,現在會產生編譯錯誤。
public class C
{
public void f(ref dynamic a)
{
}
public void M(dynamic d)
{
f(d); // error CS1620: Argument 1 must be passed with the 'ref' keyword
}
}
實作 IEnumerable
型別的集合表達式必須具有可隱含轉換成 object
的元素。
Visual Studio 2022 17.10 版中引進
將集合表示式轉換成 實作struct
且class
強型System.Collections.IEnumerable
的或GetEnumerator()
,需要集合表示式中的元素可以隱式轉換為object
。
以 IEnumerable
為目標的集合表達式元素被假定可以轉換為 object
,並且只有在系結至相應的 Add
方法時才進行轉換。
這項額外需求表示,集合表達式轉換到 IEnumerable
實作時,其處理方式會與其他目標類型一致,其中集合表達式中的項目必須能隱含地轉換為目標類型的 反覆運算類型。
此變更會影響以 IEnumerable
實作為目標的集合表達式,其中元素依賴目標型別轉換為強型別 Add
方法參數類型。
在下列範例中,報告的錯誤是無法將 _ => { }
隱式轉換為 object
。
class Actions : IEnumerable
{
public void Add(Action<int> action);
// ...
}
Actions a = [_ => { }]; // error CS8917: The delegate type could not be inferred.
若要解決錯誤,可以明確輸入元素表達式。
a = [(int _) => { }]; // ok
a = [(Action<int>)(_ => { })]; // ok
集合表達式目標類型必須具有建構函式和 Add
方法
Visual Studio 2022 17.10 版中引進
將集合表達式轉換成struct
或class
,其實作System.Collections.IEnumerable
且不具有CollectionBuilderAttribute
,需要目標型別有一個可存取的建構函式,該建構函式可以在沒有參數的情況下呼叫;而如果集合表達式不是空的,則目標型別必須有一個可存取的Add
方法,該方法可以用單一參數呼叫。
先前,建構函式和 Add
方法需要 建 構集合實例,但不需要 進行轉換。
這表示下列呼叫模棱兩可,因為 char[]
和 string
都是集合表達式的有效目標類型。
呼叫不再模棱兩可,因為 string
沒有無參數建構函式或 Add
方法。
Print(['a', 'b', 'c']); // calls Print(char[])
static void Print(char[] arg) { }
static void Print(string arg) { }
ref
自變數可以傳遞至 in
參數
Visual Studio 2022 17.8p2 版中引進
當ref readonly
設定為 12 或更新版本時,功能會放寬多載解析,允許ref
自變數傳遞至in
參數。
這可能會導致行為或來源中斷性變更:
var i = 5;
System.Console.Write(new C().M(ref i)); // prints "E" in C# 11, but "C" in C# 12
System.Console.Write(E.M(new C(), ref i)); // workaround: prints "E" always
class C
{
public string M(in int i) => "C";
}
static class E
{
public static string M(this C c, ref int i) => "E";
}
var i = 5;
System.Console.Write(C.M(null, ref i)); // prints "1" in C# 11, but fails with an ambiguity error in C# 12
System.Console.Write(C.M((I1)null, ref i)); // workaround: prints "1" always
interface I1 { }
interface I2 { }
static class C
{
public static string M(I1 o, ref int x) => "1";
public static string M(I2 o, in int x) => "2";
}
在異步編程中,應優先選擇基於模式的處置方式,而非介面型 using
Visual Studio 2022 17.10p3 版中引進
異步 using
偏好使用模式型 DisposeAsync()
方法系結,而不是以介面為基礎的 IAsyncDisposable.DisposeAsync()
。
例如,會挑選公用 DisposeAsync()
方法,而不是私用介面實作:
await using (var x = new C()) { }
public class C : System.IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
public async ValueTask DisposeAsync()
{
Console.WriteLine("PICKED");
await Task.Yield();
}
}