다음을 통해 공유


.NET 6.0.100부터 .NET 7.0.100까지 Roslyn의 주요 변경 내용

이 문서는 .NET 6 정식 릴리스(.NET SDK 버전 6.0.100)부터 .NET 7 정식 릴리스(.NET SDK 버전 7.0.100)까지 Roslyn에서 호환성을 깨는 알려진 변경 사항을 나열합니다.

제한된 형식의 모든 로컬은 비동기 메서드에서 허용되지 않습니다.

Visual Studio 2022 버전 17.6p1에 도입

제한된 형식의 로컬은 비동기 메서드에서 허용되지 않습니다. 그러나 이전 버전에서는 컴파일러가 암시적으로 선언된 일부 로컬을 알아채지 못했습니다. 예를 들어, foreach 또는 using 문장이나 해체에서.
이제 암시적으로 선언된 지역 변수도 허용되지 않습니다.

ref struct RefStruct { public void Dispose() { } }
public class C 
{
    public async Task M() 
    {
        RefStruct local = default; // disallowed
        using (default(RefStruct)) { } // now disallowed too ("error CS9104: A using statement resource of this type cannot be used in async methods or async lambda expressions")
    }
}

https://github.com/dotnet/roslyn/pull/66264를 참조하세요.

포인터는 항상 안전하지 않은 컨텍스트에 있어야 합니다.

Visual Studio 2022 버전 17.6에 도입

이전 SDK에서 컴파일러는 해당 위치를 안전하지 않은 것으로 명시적으로 표시하지 않고 포인터를 참조할 수 있는 위치를 때때로 허용했습니다. 이제 unsafe 수식자가 있어야 합니다.
예를 들어 using Alias = List<int*[]>; 합법적인 것으로 using unsafe Alias = List<int*[]>; 변경해야 합니다.
와 같은 void Method(Alias a) ... 사용법은 .로 변경 unsafe void Method(Alias a) ...해야 합니다.

규칙은 using 별칭 선언(C# 12 이전에는 unsafe 한정자를 허용하지 않았음)을 제외하고 무조건적입니다.
따라서 선언의 경우 using 언어 버전이 C# 12 이상으로 선택된 경우에만 규칙이 적용됩니다.

관리 대상으로 간주되는 System.TypedReference

Visual Studio 2022 버전 17.6에 도입

앞으로 이동하면 System.TypedReference 형식이 관리되는 것으로 간주됩니다.

unsafe
{
    TypedReference* r = null; // warning: This takes the address of, gets the size of, or declares a pointer to a managed type
    var a = stackalloc TypedReference[1]; // error: Cannot take the address of, get the size of, or declare a pointer to a managed type
}

참조 안전 오류는 람다 식에서 대리자로의 변환에 영향을 미치지 않습니다.

Visual Studio 2022 버전 17.5에 도입

람다 본문에 보고된 참조 안전 오류는 더 이상 람다 식을 대리자 형식으로 변환할 수 있는지 여부에 영향을 주지 않습니다. 이 변경 내용은 오버로드 해석에 영향을 줄 수 있습니다.

아래 예제에서는 Visual Studio 17.5에서 호출 M(x => ...) 가 모호하게 됩니다. 이는 M(D1) 호출이 람다 본문 내에서 M(D2) 와 함께 참조 안전을 발생시키더라도, F(ref x, ref y)M(D1) 둘 다 적용 가능한 것으로 간주되기 때문입니다. 비교를 위한 참조는 예제 d1d2에서 확인할 수 있습니다. 이전에는 M(D2) 오버로드가 적용되지 않는 것으로 간주되어 호출이 명확하게 M(D1)에 바인딩되었습니다.

using System;

ref struct R { }

delegate R D1(R r);
delegate object D2(object o);

class Program
{
    static void M(D1 d1) { }
    static void M(D2 d2) { }

    static void F(ref R x, ref Span<int> y) { }
    static void F(ref object x, ref Span<int> y) { }

    static void Main()
    {
        // error CS0121: ambiguous between: 'M(D1)' and 'M(D2)'
        M(x =>
            {
                Span<int> y = stackalloc int[1];
                F(ref x, ref y);
                return x;
            });

        D1 d1 = x1 =>
            {
                Span<int> y1 = stackalloc int[1];
                F(ref x1, ref y1); // error CS8352: 'y2' may expose referenced variables
                return x1;
            };

        D2 d2 = x2 =>
            {
                Span<int> y2 = stackalloc int[1];
                F(ref x2, ref y2); // ok: F(ref object x, ref Span<int> y)
                return x2;
            };
    }
}

오버로드 해석 변경 문제를 해결하려면 람다 매개 변수 또는 델리게이트에 명시적 유형을 사용하세요.

        // ok: M(D2)
        M((object x) =>
            {
                Span<int> y = stackalloc int[1];
                F(ref x, ref y); // ok: F(ref object x, ref Span<int> y)
                return x;
            });

줄의 시작 부분에 있는 원시 문자열 보간입니다.

Visual Studio 2022 버전 17.5에 도입

.NET SDK 7.0.100 이하에서는 다음이 잘못 허용되었습니다.

var x = $"""
    Hello
{1 + 1}
    World
    """;

이는 보간이 시작되는 위치를 포함하여 줄 내용이 최종 """; 줄과 동일한 공백으로 시작해야 한다는 규칙을 위반했습니다. 이제 위의 내용이 다음과 같이 작성되어야 합니다.

var x = $"""
    Hello
    {1 + 1}
    World
    """;

메서드에 대해 유추된 대리자 형식에는 기본 매개 변수 값 및 params 한정자가 포함됩니다.

Visual Studio 2022 버전 17.5에 도입

.NET SDK 7.0.100 이전 버전에서 메서드에서 유추된 대리자 형식은 다음 코드에 설명된 대로 기본 매개 변수 값 및 params 한정자를 무시했습니다.

void Method(int i = 0, params int[] xs) { }
var action = Method; // System.Action<int, int[]>
DoAction(action, 1); // ok
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });

.NET SDK 7.0.200 이상에서 이러한 메서드는 동일한 기본 매개 변수 값 및 params 한정자를 사용하여 익명 합성 대리자 형식으로 유추됩니다. 이렇게 변경하면 아래와 같이 위의 코드가 중단될 수 있습니다.

void Method(int i = 0, params int[] xs) { }
var action = Method; // delegate void <anonymous delegate>(int arg1 = 0, params int[] arg2)
DoAction(action, 1); // error CS1503: Argument 1: cannot convert from '<anonymous delegate>' to 'System.Action<int, int[]>'
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });

관련 제안에서 이 변경 내용에 대해 자세히 알아볼 수 있습니다.

명확한 할당 분석을 위해 비동기 로컬 함수의 호출은 더 이상 대기 중인 것으로 처리되지 않습니다.

Visual Studio 2022 버전 17.5에 도입

명확한 할당 분석을 위해 비동기 로컬 함수의 호출은 더 이상 대기 중인 것으로 처리되지 않으므로 로컬 함수는 완전히 실행된 것으로 간주되지 않습니다. 근거는 https://github.com/dotnet/roslyn/issues/43697를 참조하세요.

이제 아래 코드에서 명확한 할당 오류를 보고합니다.

    public async Task M()
    {
        bool a;
        await M1();
        Console.WriteLine(a); // error CS0165: Use of unassigned local variable 'a'  

        async Task M1()
        {
            if ("" == String.Empty)
            {
                throw new Exception();
            }
            else
            {
                a = true;
            }
        }
    }

INoneOperation 특성에 대한 노드는 이제 IAttributeOperation 노드입니다.

Visual Studio 2022 버전 17.5, .NET SDK 버전 7.0.200에 도입

이전 버전의 컴파일러에서는 특성의 IOperation 트리가 INoneOperation 노드로 루팅되었습니다. 특성에 대한 기본 지원을 추가했습니다. 즉, 트리의 루트가 이제 IAttributeOperation입니다. 이전 버전의 .NET SDK 분석기를 포함한 일부 분석기는 이 트리 셰이프를 기대하지 않으며, 이 트리 셰이프가 발견되면 잘못 경고(또는 경고하지 않을 수 있습니다)합니다. 이에 대한 해결 방법은 다음과 같습니다.

  • 가능한 경우 분석기 버전을 업데이트합니다. .NET SDK 또는 이전 버전의 Microsoft.CodeAnalysis.FxCopAnalyzers를 사용하는 경우 Microsoft.CodeAnalysis.NetAnalyzers 7.0.0-preview1.22464.1 이상으로 업데이트합니다.
  • 이 변경 사항을 고려한 버전으로 업데이트될 때까지 분석기에서 가양성을 억제합니다.

구조체에 대한 ref 형식 테스트는 지원되지 않습니다.

Visual Studio 2022 버전 17.4에 도입

ref 구조체 형식이 'is' 또는 'as' 연산자에서 사용되는 경우 일부 시나리오에서 컴파일러는 이전에 런타임에 항상 실패하는 형식 테스트에 대한 잘못된 경고를 보고하고 실제 형식 검사를 생략하고 잘못된 동작을 초래했습니다. 실행 시 잘못된 동작이 가능하면 컴파일러에서 오류를 대신 생성합니다.

ref struct G<T>
{
    public void Test()
    {
        if (this is G<int>) // Will now produce an error, used to be treated as always `false`.
        {

ref 로컬에서 사용되지 않은 결과는 역참조입니다.

Visual Studio 2022 버전 17.4에 도입

ref 지역 변수가 값으로 참조되었을 때 결과가 사용되지 않는 경우(예: 폐기물에 할당됨), 이전에는 이 결과가 무시되었습니다. 이제 컴파일러는 해당 로컬을 역참조하여 부작용이 관찰되도록 합니다.

ref int local = Unsafe.NullRef<int>();
_ = local; // Will now produce a `NullReferenceException`

형식의 이름을 지정할 수 없습니다. scoped

Visual Studio 2022 버전 17.4에 도입되었습니다. C# 11부터 형식의 이름을 지정할 scoped수 없습니다. 컴파일러는 이러한 모든 형식 이름에 오류를 보고합니다. 이 문제를 해결하려면 형식 이름과 모든 사용을 @으로 이스케이프 처리해야 합니다.

class scoped {} // Error CS9056
class @scoped {} // No error
ref scoped local; // Error
ref scoped.nested local; // Error
ref @scoped local2; // No error

이 작업은 이제 변수 선언의 한정자가 되었으며, ref 형식에서 scoped 뒤에 예약되었으므로 ref가 수행되었습니다.

형식의 이름을 지정할 수 없습니다. file

Visual Studio 2022 버전 17.4에 도입되었습니다. C# 11부터 형식의 이름을 지정할 file수 없습니다. 컴파일러는 이러한 모든 형식 이름에 오류를 보고합니다. 이 문제를 해결하려면 형식 이름과 모든 사용을 @으로 이스케이프 처리해야 합니다.

class file {} // Error CS9056
class @file {} // No error

file가 이제 형식 선언의 한정자로 사용됩니다.

이와 관련된 csharplang 문제의 변경 내용에 대해 자세히 알아볼 수 있습니다.

#line 명령어에서 필수 공백

.NET SDK 6.0.400, Visual Studio 2022 버전 17.3에 도입되었습니다.

#line 범위 지시문이 C# 10에서 도입되었을 때 특정 간격이 필요하지 않았습니다.
예를 들어 다음과 같이 유효합니다. #line(1,2)-(3,4)5"file.cs"

Visual Studio 17.3에서 컴파일러에는 첫 번째 괄호, 문자 오프셋 및 파일 이름 앞에 공백이 필요합니다.
따라서 위의 예제는 공백이 추가 #line (1,2)-(3,4) 5 "file.cs"되지 않는 한 구문 분석하지 못합니다.

System.IntPtr 및 System.UIntPtr에서 확인된 연산자

.NET SDK 7.0.100, Visual Studio 2022 버전 17.3에 도입되었습니다.

플랫폼이 숫자IntPtrUIntPtr 형식을 지원하는지 여부는 System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr 존재 여부로 표시됩니다. 이 경우, nintnuint에 적용되는 내장 연산자는 해당 기본 형식에 적용됩니다. 이는 이러한 플랫폼에서 오버플로가 발생할 때 throw할 수 있는 기본 제공 IntPtr 연산자가 UIntPtrchecked에 내장되어 있음을 의미합니다.

IntPtr M(IntPtr x, int y)
{
    checked
    {
        return x + y; // may now throw
    }
}

unsafe IntPtr M2(void* ptr)
{
    return checked((IntPtr)ptr); // may now throw
}

가능한 해결 방법은 다음과 같습니다.

  1. 컨텍스트 지정 unchecked
  2. 숫자 IntPtr/UIntPtr 형식이 없는 플랫폼/TFM으로 다운그레이드

또한 다른 숫자 형식 간의 IntPtr/UIntPtr 암시적 변환은 이러한 플랫폼에서 표준 변환으로 처리됩니다. 경우에 따라 오버로드 해결에 영향을 줄 수 있습니다.

이러한 변경으로 인해 확인되지 않은 컨텍스트에서 사용자 코드가 오버플로 예외에 의존하고 있을 경우 또는 확인된 컨텍스트에서 오버플로 예외가 발생하지 않을 것으로 예상할 경우 코드의 동작이 변경될 수 있습니다. 이러한 동작 변경을 감지하고 적절한 조치를 취할 수 있도록 분석기가 7.0에 추가 되었습니다. 분석기는 잠재적인 동작 변경에 대한 진단을 생성합니다. 이는 기본적으로 정보 심각도로 설정되지만 editorconfig를 통해 경고로 업그레이드할 수 있습니다.

System.UIntPtr 및 System.Int32 추가

.NET SDK 7.0.100, Visual Studio 2022 버전 17.3에 도입되었습니다.

플랫폼이 숫자IntPtrUIntPtr 형식을 지원하고 System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr이 존재하는 경우, +(UIntPtr, int)에서 정의된 연산자 System.UIntPtr는 더 이상 사용할 수 없습니다. 대신 형식 System.UIntPtrSystem.Int32 의 식을 추가하면 오류가 발생합니다.

UIntPtr M(UIntPtr x, int y)
{
    return x + y; // error: Operator '+' is ambiguous on operands of type 'nuint' and 'int'
}

가능한 해결 방법은 다음과 같습니다.

  1. UIntPtr.Add(UIntPtr, int) 메서드를 사용합니다: UIntPtr.Add(x, y)
  2. 두 번째 피연산자에 대해 형식 nuint에 검사되지 않은 캐스트를 적용합니다: x + unchecked((nuint)y)

메서드 또는 로컬 함수의 특성에 있는 Nameof 연산자

.NET SDK 6.0.400, Visual Studio 2022 버전 17.3에 도입되었습니다.

언어 버전이 C# 11 이상일 때, 메서드의 특성에 있는 nameof 연산자는 해당 메서드의 형식 매개 변수를 범위에 가져옵니다. 로컬 함수도 마찬가지입니다.
nameof 연산자가 메서드의 속성, 형식 매개변수, 또는 매개변수에 있을 때, 이 연산자는 해당 메서드의 매개변수를 해당 범위 내에서 사용할 수 있게 합니다. 로컬 함수, 람다, 대리자 및 인덱서에도 동일하게 적용됩니다.

예를 들어, 이제 다음과 같은 예시들이 오류로 간주됩니다.

class C
{
  class TParameter
  {
    internal const string Constant = """";
  }
  [MyAttribute(nameof(TParameter.Constant))]
  void M<TParameter>() { }
}
class C
{
  class parameter
  {
    internal const string Constant = """";
  }
  [MyAttribute(nameof(parameter.Constant))]
  void M(int parameter) { }
}

가능한 해결 방법은 다음과 같습니다.

  1. 외부 범위에서 이름을 숨기지 않도록 형식 매개 변수 또는 매개 변수의 이름을 바꿉니다.
  2. 연산자 대신 문자열 리터럴을 nameof 사용합니다.

참조로 out 매개 변수를 반환할 수 없음

.NET SDK 7.0.100, Visual Studio 2022 버전 17.3에 도입되었습니다.

언어 버전 C# 11 이상 또는 .NET 7.0 이상을 사용하는 경우 참조로 매개 변수를 out 반환할 수 없습니다.

static ref T ReturnOutParamByRef<T>(out T t)
{
    t = default;
    return ref t; // error CS8166: Cannot return a parameter by reference 't' because it is not a ref parameter
}

가능한 해결 방법은 다음과 같습니다.

  1. System.Diagnostics.CodeAnalysis.UnscopedRefAttribute를 사용하여 참조를 범위가 지정되지 않은 것으로 표시합니다.

    static ref T ReturnOutParamByRef<T>([UnscopedRef] out T t)
    {
        t = default;
        return ref t; // ok
    }
    
  2. 메서드 시그니처를 변경하여 매개 변수를 전달합니다 ref.

    static ref T ReturnRefParamByRef<T>(ref T t)
    {
        t = default;
        return ref t; // ok
    }
    

ref 구조체의 인스턴스 메서드는 범위가 지정되지 않은 ref 매개 변수를 캡처할 수 있습니다.

.NET SDK 7.0.100, Visual Studio 2022 버전 17.4에 도입되었습니다.

언어 버전이 C# 11 이상이거나 .NET 7.0 이상에서 ref struct 인스턴스 메서드를 호출할 때, 이는 범위가 지정되지 않은 ref 또는 in 매개변수를 캡처하는 것으로 간주됩니다.

R<int> Use(R<int> r)
{
    int i = 42;
    r.MayCaptureArg(ref i); // error CS8350: may expose variables referenced by parameter 't' outside of their declaration scope
    return r;
}

ref struct R<T>
{
    public void MayCaptureArg(ref T t) { }
}

매개 변수가 ref 인스턴스 메서드에서 캡처되지 않은 경우 가능한 해결 방법은 매개 변수를 in 또는 ref struct 대신 scoped ref 또는 scoped in로 선언하는 것입니다.

R<int> Use(R<int> r)
{
    int i = 42;
    r.CannotCaptureArg(ref i); // ok
    return r;
}

ref struct R<T>
{
    public void CannotCaptureArg(scoped ref T t) { }
}

메서드 ref 구조체 반환 이스케이프 분석은 ref 인수의 ref 이스케이프에 따라 달라집니다.

.NET SDK 7.0.100, Visual Studio 2022 버전 17.4에 도입되었습니다.

언어 버전 C# 11 이상 또는 .NET 7.0 이상을 사용할 때, 메서드 호출에서 반환 값이나 ref struct 매개변수로 반환되는 out이(가) 안전하게 이스케이프할 수 있는 조건은 메서드 호출의 모든 ref 인수가 in 조건을 만족할 때뿐입니다. 인수에는 in 암시적 기본 매개 변수 값이 포함될 수 있습니다.

ref struct R { }

static R MayCaptureArg(ref int i) => new R();

static R MayCaptureDefaultArg(in int i = 0) => new R();

static R Create()
{
    int i = 0;
    // error CS8347: Cannot use a result of 'MayCaptureArg(ref int)' because it may expose
    // variables referenced by parameter 'i' outside of their declaration scope
    return MayCaptureArg(ref i);
}

static R CreateDefault()
{
    // error CS8347: Cannot use a result of 'MayCaptureDefaultArg(in int)' because it may expose
    // variables referenced by parameter 'i' outside of their declaration scope
    return MayCaptureDefaultArg();
}

ref 또는 in 인수가 ref struct 반환 값에 캡처되지 않은 경우 가능한 해결 방법은 매개 변수를 scoped ref 또는 scoped in로 선언하는 것입니다.

static R CannotCaptureArg(scoped ref int i) => new R();

static R Create()
{
    int i = 0;
    return CannotCaptureArg(ref i); // ok
}

ref 에서 ref struct 범위가 지정되지 않은 것으로 간주되는 인수 __arglist

.NET SDK 7.0.100, Visual Studio 2022 버전 17.4에 도입되었습니다.

언어 버전 C# 11 이상 또는 .NET 7.0 이상을 사용하는 경우, ref에서 ref struct 형식으로 변환한 후 __arglist에 인수로 전달될 때 범위가 지정되지 않은 참조로 간주됩니다.

ref struct R { }

class Program
{
    static void MayCaptureRef(__arglist) { }

    static void Main()
    {
        var r = new R();
        MayCaptureRef(__arglist(ref r)); // error: may expose variables outside of their declaration scope
    }
}

부호 없는 오른쪽 시프트 연산자

.NET SDK 6.0.400, Visual Studio 2022 버전 17.3에 도입되었습니다. 언어는 "서명되지 않은 오른쪽 시프트" 연산자(>>>)에 대한 지원을 추가했습니다. 사용자 정의 "부호 없는 오른쪽 시프트" 연산자를 구현하는 메서드를 일반 메서드로 사용할 수 없도록 기능을 비활성화합니다.

예를 들어, VB 또는 C#이 아닌 일부 언어로 개발된 기존 라이브러리가 형식 C1에 대해 "서명되지 않은 오른쪽 시프트" 사용자 정의 연산자를 제공합니다. 다음 코드는 이전에 성공적으로 컴파일하는 데 사용됩니다.

static C1 Test1(C1 x, int y) => C1.op_UnsignedRightShift(x, y); //error CS0571: 'C1.operator >>>(C1, int)': cannot explicitly call operator or accessor

가능한 해결 방법은 >>> 연산자를 사용하는 것으로 전환하는 것입니다.

static C1 Test1(C1 x, int y) => x >>> y;

참조 구조체로서의 Foreach 열거자

.NET SDK 6.0.300, Visual Studio 2022 버전 17.2에 도입되었습니다.foreach ref 구조체 열거자 형식을 사용하는 경우 언어 버전이 7.3 이상으로 설정된 경우 오류를 보고합니다.

이렇게 하면 지원 전에 C# 버전을 대상으로 하는 최신 컴파일러에서 기능이 지원되는 버그가 수정됩니다.

가능한 해결 방법은 다음과 같습니다.

  1. ref struct 유형을 struct 또는 class 유형으로 변경합니다.
  2. <LangVersion> 요소를 7.3 이상으로 업그레이드합니다.

비동기 foreach는 인터페이스 DisposeAsync의 명시적 구현보다 패턴 IAsyncDisposable.DisposeAsync() 기반을 선호합니다.

.NET SDK 6.0.300, Visual Studio 2022 버전 17.2에 도입되었습니다. 비동기 foreach 에서는 패턴 기반 DisposeAsync() 메서드를 사용하는 대신 IAsyncDisposable.DisposeAsync()바인딩하는 것을 선호합니다.

예를 들어 다음의 DisposeAsync() 메서드가 아닌 IAsyncEnumerator<int>.DisposeAsync() 선택됩니다.AsyncEnumerator

await foreach (var i in new AsyncEnumerable())
{
}

struct AsyncEnumerable
{
    public AsyncEnumerator GetAsyncEnumerator() => new AsyncEnumerator();
}

struct AsyncEnumerator : IAsyncDisposable
{
    public int Current => 0;
    public async ValueTask<bool> MoveNextAsync()
    {
        await Task.Yield();
        return false;
    }
    public async ValueTask DisposeAsync()
    {
        Console.WriteLine("PICKED");
        await Task.Yield();
    }
    ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
}

이 변경은 공용 DisposeAsync 메서드가 선언된 형식에 표시되는 사양 위반을 수정하는 반면 명시적 인터페이스 구현은 인터페이스 형식에 대한 참조를 통해서만 표시됩니다.

이 오류를 해결하려면 형식에서 패턴 기반 DisposeAsync 메서드를 제거합니다.

변환된 문자열을 기본 인수로 허용하지 않음

.NET SDK 6.0.300, Visual Studio 2022 버전 17.2에 도입되었습니다. C# 컴파일러는 문자열 상수의 참조 변환과 관련된 잘못된 기본 인수 값을 허용하고 원본에 지정된 기본값 대신 상수 값으로 내보 null 낸다. Visual Studio 17.2에서는 오류가 발생합니다. roslyn#59806을 참조하세요.

이 변경은 컴파일러의 사양 위반을 수정합니다. 기본 인수는 컴파일 시간 상수여야 합니다. 이전 버전에서는 다음 코드를 허용했습니다.

void M(IEnumerable<char> s = "hello")

앞의 선언에는 string에서 IEnumerable<char>으로의 변환이 필요했습니다. 컴파일러는 이 구문을 허용하고 인수의 값으로 null를 내보냅니다. 위의 코드는 17.2부터 컴파일러 오류를 생성합니다.

이 변경을 해결하려면 다음 변경 중 하나를 수행할 수 있습니다.

  1. 변환이 필요하지 않도록 매개 변수 형식을 변경합니다.
  2. 기본 인수의 값을 변경하여 null 이전 동작을 복원합니다.

명시적 람다 반환 형식인 상황별 키워드 var

.NET SDK 6.0.200, Visual Studio 2022 버전 17.1에 도입되었습니다. 컨텍스트 키워드 var은 명시적 람다 반환 형식으로 사용할 수 없습니다.

이 변경은 가 람다 식의 반환 형식으로 자연형을 유지하도록 함으로써 var을 가능하게 합니다.

명명된 var 형식이 있고 명시적 반환 var 형식(형식)을 사용하여 람다 식을 정의하는 경우 이 오류가 발생할 수 있습니다.

using System;

F(var () => default);  // error CS8975: The contextual keyword 'var' cannot be used as an explicit lambda return type
F(@var () => default); // ok
F(() => default);      // ok: return type is inferred from the parameter to F()

static void F(Func<var> f) { }

public class var
{
}

해결 방법은 다음과 같은 변경 내용을 포함합니다.

  1. 반환 형식으로 사용합니다 @var .
  2. 컴파일러가 반환 형식을 결정하게 하려면 명시적 반환 형식을 제거합니다.

보간된 문자열 처리기 및 인덱서 초기화

.NET SDK 6.0.200, Visual Studio 2022 버전 17.1에 도입되었습니다. 보간된 문자열 처리기를 사용하고 생성자에 대한 입력으로 수신기를 요구하는 인덱서는 개체 이니셜라이저에서 사용할 수 없습니다.

이 변경은 인덱서 이니셜라이저가 보간된 문자열 처리기를 사용하고 보간된 문자열 처리기가 인덱서의 수신기를 생성자의 매개 변수로 사용하는 에지 사례 시나리오를 허용하지 않습니다. 이 변경의 이유는 이 시나리오에서 아직 초기화되지 않은 변수에 액세스할 수 있기 때문입니다. 다음 예제를 고려하세요.

using System.Runtime.CompilerServices;

// error: Interpolated string handler conversions that reference
// the instance being indexed cannot be used in indexer member initializers.
var c = new C { [$""] = 1 }; 

class C
{
    public int this[[InterpolatedStringHandlerArgument("")] CustomHandler c]
    {
        get => ...;
        set => ...;
    }
}

[InterpolatedStringHandler]
class CustomHandler
{
    // The constructor of the string handler takes a "C" instance:
    public CustomHandler(int literalLength, int formattedCount, C c) {}
}

해결 방법은 다음과 같은 변경 내용을 포함합니다.

  1. 보간된 문자열 처리기에서 수신기 형식을 제거합니다.
  2. 인수를 string로 인덱서에 변경하세요.

ref, readonly ref, in, out은 비관리자 호출자만 있는 메서드에서 매개 변수나 반환 값으로 허용되지 않습니다

.NET SDK 6.0.200, Visual Studio 2022 버전 17.1에 도입되었습니다.ref/ref readonly/in/out 로 특성 UnmanagedCallersOnly이 지정된 메서드의 반환/매개 변수에는 사용할 수 없습니다.

이 변경은 버그 수정입니다. 반환 값 및 매개 변수는 Blittable이 아닙니다. 인수 또는 반환 값을 참조로 전달하면 정의되지 않은 동작이 발생할 수 있습니다. 다음 선언 중 어느 것도 컴파일되지 않습니다.

using System.Runtime.InteropServices;
[UnmanagedCallersOnly]
static ref int M1() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static ref readonly int M2() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M3(ref int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M4(in int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M5(out int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

해결 방법은 참조별 한정자를 제거하는 것입니다.

패턴에서 길이와 개수는 음수가 아닌 것으로 간주됩니다.

.NET SDK 6.0.200, Visual Studio 2022 버전 17.1에 도입되었습니다.Length 계산 가능 및 Count 인덱싱 가능한 형식의 속성은 패턴 및 스위치의 소급 및 완전성 분석을 위해 음수가 아닌 것으로 간주됩니다. 이러한 형식은 암시적 인덱스 인덱서 및 목록 패턴과 함께 사용할 수 있습니다.

LengthCount 속성은 int 형식임에도 불구하고 패턴을 분석할 때는 음수가 아닌 것으로 간주됩니다. 다음 샘플 방법을 고려합니다.

string SampleSizeMessage<T>(IList<T> samples)
{
    return samples switch
    {
        // This switch arm prevents a warning before 17.1, but will never happen in practice.
        // Starting with 17.1, this switch arm produces a compiler error.
        // Removing it won't introduce a warning.
        { Count: < 0 }    => throw new InvalidOperationException(),
        { Count:  0 }     => "Empty collection",
        { Count: < 5 }    => "Too small",
        { Count: < 20 }   => "reasonable for the first pass",
        { Count: < 100 }  => "reasonable",
        { Count: >= 100 } => "fine",
    };
}

void M(int[] i)
{
    if (i is { Length: -1 }) {} // error: impossible under assumption of non-negative length
}

17.1 이전에는 모든 가능한 값이 포함되지 않았다는 경고를 피하기 위해, Count가 음수인지 확인하는 첫 번째 스위치 암 테스트가 필요했습니다. 17.1부터 첫 번째 스위치 암은 컴파일러 오류를 생성합니다. 해결 방법은 잘못된 사례에 대해 추가된 스위치 암을 제거하는 것입니다.

이 변경 내용은 목록 패턴 추가의 일부로 수행되었습니다. 컬렉션의 Length 또는 Count 속성이 모두 비음수로 간주되는 경우 처리 규칙이 더 일관적입니다. 언어 디자인 문제의 변경 내용에 대한 자세한 내용을 읽을 수 있습니다.

해결 방법은 도달할 수 없는 조건이 있는 스위치의 분기를 제거하는 것입니다.

구조체에 필드 이니셜라이저를 추가하려면 명시적으로 선언된 생성자가 필요합니다.

.NET SDK 6.0.200, Visual Studio 2022 버전 17.1에 도입되었습니다.struct 필드 이니셜라이저가 있는 형식 선언에는 명시적으로 선언된 생성자가 포함되어야 합니다. 또한 모든 필드는 이니셜라이저가 없는 struct 인스턴스 생성자에 반드시 할당 : this() 되어야 하므로 이전에 할당되지 않은 필드는 추가된 생성자 또는 필드 이니셜라이저에서 할당해야 합니다. dotnet/csharplang#5552, dotnet/roslyn#58581을 참조하세요.

C#에서 변수를 기본값으로 초기화하는 방법에는 두 가지가 new() 있습니다 default. 클래스의 경우 new는 새 인스턴스를 만들고, defaultnull를 반환하므로 차이점이 분명합니다. 구조체의 경우 각 필드/속성이 자체 기본값으로 설정된 인스턴스를 default반환하므로 구조체의 차이는 더 미묘합니다. C# 10에서 구조체에 대한 필드 이니셜라이저를 추가했습니다. 필드 이니셜라이저는 명시적으로 선언된 생성자가 실행되는 경우에만 실행됩니다. 중요한 것은 default를 사용하거나 어떤 struct형식의 배열을 만들 때 실행되지 않는다는 점입니다.

17.0에서 필드 이니셜라이저가 있지만 선언된 생성자가 없는 경우 필드 이니셜라이저를 실행하는 매개 변수 없는 생성자가 합성됩니다. 그러나 생성자 선언을 추가하거나 제거하면 매개 변수가 없는 생성자가 합성되는지 여부에 영향을 줄 수 있으며 결과적으로 동작 new()이 변경될 수 있습니다.

이 문제를 해결하기 위해 .NET SDK 6.0.200(VS 17.1)에서 컴파일러는 더 이상 매개 변수 없는 생성자를 합성하지 않습니다. struct 필드 이니셜라이저가 포함되어 있고 명시적 생성자가 없는 경우 컴파일러는 오류를 생성합니다. struct가 필드 이니셜라이저를 가지고 있다면 생성자를 선언해야 합니다. 그렇지 않으면 필드 이니셜라이저가 실행되지 않습니다.

또한 생성자에 struct 이니셜라이저가 없는 경우, 필드 이니셜라이저가 없는 모든 필드는 각 : this() 생성자에서 할당되어야 합니다.

예를 들면 다음과 같습니다.

struct S // error CS8983: A 'struct' with field initializers must include an explicitly declared constructor.
{
    int X = 1; 
    int Y;
}

해결 방법은 생성자를 선언하는 것입니다. 이전에 필드를 할당하지 않은 경우 이 생성자는 빈 매개 변수가 없는 생성자가 될 수 있으며 종종 발생합니다.

struct S
{
    int X = 1;
    int Y;

    public S() { Y = 0; } // ok
}

서식 지정자에 중괄호를 포함할 수 없습니다.

.NET SDK 6.0.200, Visual Studio 2022 버전 17.1에 도입되었습니다. 보간된 문자열의 서식 지정자는 중괄호({ 또는 })를 포함할 수 없습니다. 이전 버전에서는 {{ 를 이스케이프된 { 로, }} 를 이스케이프된 } 문자로 형식 지정자 내에서 해석했습니다. 이제 형식 지정자의 첫 번째 } 문자가 보간을 종료하고 모든 { 문자가 오류입니다.

이렇게 하면 보간된 문자열 처리가 System.String.Format 처리와 일관성을 갖습니다.

using System;
Console.WriteLine($"{{{12:X}}}");
//prints now: "{C}" - not "{X}}"

X 는 대문자 16진수의 형식이며 C 12의 16진수 값입니다.

해결 방법은 형식 문자열에서 추가 중괄호를 제거하는 것입니다.

이와 관련된 roslyn 문제의 변경 내용에 대해 자세히 알아볼 수 있습니다.

형식의 이름을 지정할 수 없습니다. required

Visual Studio 2022 버전 17.3에 도입되었습니다. C# 11부터 형식의 이름을 지정할 required수 없습니다. 컴파일러는 이러한 모든 형식 이름에 오류를 보고합니다. 이 문제를 해결하려면 형식 이름과 모든 사용을 @으로 이스케이프 처리해야 합니다.

class required {} // Error CS9029
class @required {} // No error

이 작업은 이제 속성 및 필드의 멤버 한정자로 required가 사용됩니다.

이와 관련된 csharplang 문제의 변경 내용에 대해 자세히 알아볼 수 있습니다.