自 .NET 8.0.100 以来到 .NET 9.0.100 的 Roslyn 重大更改

本文档列出了在 .NET 8 的常规版本(.NET SDK 版本 8.0.100)发布至 .NET 9 的常规版本(.NET SDK 版本 9.0.100)发布期间,Roslyn 中已知的破坏性变更。

记录结构类型不再允许使用 InlineArray 属性。

Visual Studio 2022 版本 17.11 中引入

[System.Runtime.CompilerServices.InlineArray(10)] // error CS9259: Attribute 'System.Runtime.CompilerServices.InlineArray' cannot be applied to a record struct.
record struct Buffer1()
{
    private int _element0;
}

[System.Runtime.CompilerServices.InlineArray(10)] // error CS9259: Attribute 'System.Runtime.CompilerServices.InlineArray' cannot be applied to a record struct.
record struct Buffer2(int p1)
{
}

迭代器在 C# 13 和更新版本中引入安全上下文

Visual Studio 2022 版本 17.11 中引入

尽管语言规范指出迭代器引入了安全上下文,但 Roslyn 不会在 C# 12 和更低版本中实现该上下文。 这将在 C# 13 中更改,作为 允许在迭代器中使用非安全代码的功能的一部分。 更改不会破坏正常方案,因为不允许在迭代器中直接使用不安全构造。 但是,它可能会破坏不安全上下文以前继承到嵌套本地函数中的场景,例如:

unsafe class C // unsafe context
{
    System.Collections.Generic.IEnumerable<int> M() // an iterator
    {
        yield return 1;
        local();
        void local()
        {
            int* p = null; // allowed in C# 12; error in C# 13
        }
    }
}

只需将 unsafe 修饰符添加到本地函数即可解决中断问题。

C# 13 及更高版本中重载解析引发的集合表达式重大变更

使用 C# 13+ 时在 Visual Studio 2022 版本 17.12 及更新版中引入

C# 13 中的集合表达式绑定存在一些更改。 其中大多数将多义性转化为成功的编译,但也有一些是重大更改,这些更改要么导致新的编译错误,要么是行为重大更改。 下面将详细介绍。

无论 API 是否是打破重载的跨度,都不再使用空集合表达式

当向重载方法提供空集合表达式,并且没有明确的元素类型时,我们不再根据 API 是否接受 ReadOnlySpan<T>Span<T> 来决定是否优先选择该 API。 例如:

class C
{
    static void M(ReadOnlySpan<int> ros) {}
    static void M(Span<object> s) {}

    static void Main()
    {
        M([]); // C.M(ReadOnlySpan<int>) in C# 12, error in C# 13.
    }
}

确切元素类型优先于其他所有类型

在 C# 13 中,我们更喜欢精确的元素类型匹配,并查看来自表达式的转换。 这可能会导致在涉及常量时发生行为更改:

class C
{
    static void M1(ReadOnlySpan<byte> ros) {}
    static void M1(Span<int> s) {}

    static void M2(ReadOnlySpan<string> ros) {}
    static void M2(Span<CustomInterpolatedStringHandler> ros) {}

    static void Main()
    {
        M1([1]); // C.M(ReadOnlySpan<byte>) in C# 12, C.M(Span<int>) in C# 13

        M2([$"{1}"]); // C.M(ReadOnlySpan<string>) in C# 12, C.M(Span<CustomInterpolatedStringHandler>) in C# 13
    }
}

不再允许在没有正确声明 DefaultMemberAttribute 的情况下声明索引器。

在 Visual Studio 2022 版本 17.13 中引入

public interface I1
{
    public I1 this[I1 args] { get; } // error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor'
}

方法组自然类型中考虑了默认参数和 params 参数

在 Visual Studio 2022 版本 17.13 中引入

以前,编译器会意外推断出不同的委托类型,具体取决于使用默认参数值或 params 数组时源中候选项的顺序。 现在发出歧义错误。

using System;

class Program
{
    static void Main()
    {
        var x1 = new Program().Test1; // previously Action<long[]> - now error
        var x2 = new Program().Test2; // previously anonymous void delegate(params long[]) - now error

        x1();
        x2();
    }
}

static class E
{
    static public void Test1(this Program p, long[] a) => Console.Write(a.Length);
    static public void Test1(this object p, params long[] a) => Console.Write(a.Length);

    static public void Test2(this object p, params long[] a) => Console.Write(a.Length);
    static public void Test2(this Program p, long[] a) => Console.Write(a.Length);
}

同样,在LangVersion=12或更低的条件下,params修饰符在所有方法中必须匹配,以便推断出唯一的委托签名。 请注意,由于LangVersion=13,这不会影响及其后续内容。

var d = new C().M; // previously inferred Action<int[]> - now error CS8917: the delegate type could not be inferred

static class E
{
    public static void M(this C c, params int[] x) { }
}

class C
{
    public void M(int[] x) { }
}

解决方法是使用显式委托类型,而不是在这些情况下依靠 var 推断。

dotnet_style_require_accessibility_modifiers 现在会一致地应用于接口成员

PR:https://github.com/dotnet/roslyn/pull/76324

在此更改之前,dotnet_style_require_accessibility_modifiers 的分析器只会忽略接口成员。 这是因为 C# 最初完全不允许对接口成员使用修饰符,使其始终是公共的。

更高版本的语言放宽了此限制,允许用户在接口成员(包括冗余 public 修饰符)上提供辅助功能修饰符。

分析器已更新,现在也对接口成员强制执行此选项的值。 值的含义如下所示:

  1. never。 分析器不进行分析。 允许对所有成员使用冗余修饰符。
  2. always。 所有成员(包括接口成员)始终需要冗余修饰符。 例如: private 类成员的修饰符和 public 接口成员上的修饰符。 如果你觉得所有成员都应显式声明其可访问性,则可以使用此选项。
  3. for_non_interface_members。 对于所有不属于接口的成员,需要使用冗余修饰符,但接口成员则不得使用。 例如:private 在私有类成员上是必需的。 但是,不允许公共接口成员具有冗余 public 修饰符。 这与在语言允许在接口成员上使用修饰符之前存在的标准修饰符方法相匹配。
  4. omit_if_default。 不允许使用冗余修饰符。 例如,私有类成员将被禁止使用private,公共接口成员将被禁止使用public。 如果你认为当可访问性与语言默认选择的内容相匹配时重新声明可访问性是多余的并且应该被禁止,则可以选择使用此选项。