共用方式為


在 .NET 7.0.100 到 .NET 8.0.100 之後,Roslyn 的重大變更

本檔列出在 .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 版中引進

將集合表示式轉換成 實作structclass強型System.Collections.IEnumerableGetEnumerator(),需要集合表示式中的元素可以隱式轉換為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 版中引進

將集合表達式轉換成structclass,其實作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();
    }
}