다음을 통해 공유


.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'
}

기본 및 매개 변수 매개 변수는 메서드 그룹 자연 형식에서 고려됩니다.

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 인터페이스 멤버에 일관되게 적용됩니다.

홍보: 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사용할 수 없습니다. 기본적으로 선택한 언어와 일치할 때 접근성을 다시 표시하는 것은 중복되며 허용되지 않아야 하는 경우 사용할 수 있는 옵션입니다.