本文档列出了 .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
参数放宽了重载解析,允许在将 ref
设置为 12 或更高时将 in
个参数传递给 LangVersion
个参数。
这可能会导致行为或源代码破坏性更改:
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();
}
}