C# Next (C# 11 후보) 배우기 - slog

본 슬로그의 목적은 C# Next (C# 11 후보)를 미리 탐구하고 익숙해지기 위함입니다. C# 11이 정식 릴리즈가 되면 종료합니다.

.NET 7이 출시되지 않더라도 가장 최신의 .NET 릴리즈(현재 .NET 6.0.200, .NET 7 미리보기가 출시될 경우 가장 최신의 미리보기)를 설치한 후 아래의 문서 State가 Merged된 기능을 테스트해 볼 수 있습니다.

프로젝트 설정에

<LangVersion>preview</LangVersion>

를 추가하면 미리보기 테스트를 할 수 있게 됩니다.


좋아요 4

문자열 보간에서 개행 허용 (Newlines in interpolations)

    var result = $"{
        1 +
        2 +
        3 +
        4 +
        5 +
        6 +
        7 +
        8 +
        9 +
        10}";

    Console.WriteLine(result);

| 출력

55

한번도 이렇게 써보지 않아 몰랐는데 이전에는 개행이 안되었었습니다. 이제 문자열 보간 내에서 개행을 허용합니다.

좋아요 4

목록 패턴 (list pattern)

목록을 패턴 매칭에 사용할 수 있게 되었습니다.

| 목록 패턴

    var list = new[] { 1, 2, 3, 4, 5 };
    var list2 = new[] { 1, 2, 3, 7, 5 };

    Print(list is [1, 2, 3, 4, 5]);
    Print(list is [1, .., 5]);
    Print(list is [1, .., 4]);
    Print(list is [1, .., var x, 5] && x is 4);

    Print(list2 is [1, 2, 3, 4, 5]);
    Print(list2 is [1, .., 5]);
    Print(list2 is [1, .., var y, 5] && y is 7);

출력을 편하게 하기 위해 CallerArgumentExpression 특성을 이용했습니다. 이 특성을 이용하면 인자에 전달되는 코드를 문자열로 메소드에서 사용할 수 있습니다.

void Print(object? value, [CallerArgumentExpression("value")] string? argumentExpression = null)
{
    System.Console.WriteLine($"{argumentExpression}: {value}");
}

| 출력

list is [1, 2, 3, 4, 5]: True
list is [1, .., 5]: True
list is [1, .., 4]: False
list is [1, .., var x, 5] && x is 4: True
list2 is [1, 2, 3, 4, 5]: False
list2 is [1, .., 5]: True
list2 is [1, .., var y, 5] && y is 7: True

오… 신기합니다. 잘되네요!

좋아요 3

인자 널 체크 (!!) (Parameter null-checking)

이제 void Method(string a!!) 형태로 인자에 !!를 줘서 다음의 예외 처리를 단순화 할 수 있습니다.

void Method(string a)
{
   if (a is null)
      throw new ArgumentNullException(nameof(a));

   ...
}
void Method(string a!!)
{
   ...
}

| 코드

    Method("a", "b", "c");
    Method(null!, "b", "c");

    void Method(string a!!, string b!!, string c!!)
    {
        Console.WriteLine("정상동작");
    }

| 출력

정상동작
Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'a')
...
좋아요 2

원시 문자열 리터럴(Raw string literal)

타 언어에서,

v = """
a
b
c
"""

이런 형태로 지원하는게 C# 개발자로서 부러웠는데요, 드디어 원시 문자열 리터럴을 C#에서 지원합니다.

    var s1 = """
a
b
c
""";
    Console.WriteLine(s1);
    Console.WriteLine();

    var s2 = """
             a
             b
             c
             """;

    Console.WriteLine(s2);
    Console.WriteLine();

    var i = 1;
    var s3 = $"""
             {i++}
             {i++}
             {i++}
             """;
    Console.WriteLine(s3);

편리한 점은 s1처럼 행을 첫번째로 맞출 필요 없이 s2처럼 닫는 원시 문자열 리터럴에 맞추면 앞의 공간은 무시하게 됩니다. 이는 좀 더 깔끔한 코드 모양을 만들 수 있어서 좋은 아이디어 같아요.

image

| 출력

a
b
c

a
b
c

1
2
3
좋아요 2

제네릭 특성(Generic attributes)

이제 특성에 제네릭을 사용할 수 있게 되었습니다. 이미 .NET 6에 Preview로 기능을 테스트해볼 수 있습니다.

using System.Reflection;
using System.Runtime.CompilerServices;

Test_CS11_GenericAttribute();

[Generic<string>("test")]
[Generic<int>(25)]
[TestGeneric<TestInfo>()]
void Test_CS11_GenericAttribute()
{
    var attributesDataGroup = typeof(Program).GetRuntimeMethods().Select(x => x.GetCustomAttributesData());

    foreach (var attributesData in attributesDataGroup)
    {
        foreach (var attributeData in attributesData)
            if (attributeData.AttributeType.IsGenericType is true && attributeData.AttributeType.GetGenericTypeDefinition() == typeof(GenericAttribute<>))
                Console.WriteLine(attributeData);
    }
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
class GenericAttribute<T> : Attribute
{
    T Value { get; }

    public GenericAttribute(T value)
    {
        Value = value;
    }
}

| 출력

[GenericAttribute`1[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]("test")]
[GenericAttribute`1[[System.Int32, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]((Int32)25)]
좋아요 2

Visual Studio 17.3 미리보기 1~2에 대거 테스트가 가능한 기능들이 구현되었습니다. (아직 미리보기로 릴리즈되진 않음)

  • Unsigned 오른쪽 시프트
    >>>로 상위 비트를 복사하지 않고 오른쪽 쉬프트
  • UTF8 문자열 리터럴
    • 상수 문자열을 ReadOnlySpan로 변환할 경우 UTF8 문자열로 취급
    • u8 접미사를 붙여서 UTF8 유형으로 강제 적용
  • checked 사용자정의 연산자 지원
  • 자동 할당 기본 구조체
  • 확장된 nameof 범위
    메서드 또는 매개변수의 속성에서 nameof를 허용
  • 쉬프트 연산 요구사항 완화
    오른쪽 연산자가 더이상 int로만 제한되지 않도록 시프트 연산 요구사항이 완화됨
  • ReadonlySpan을 문자열로 switch 분기
좋아요 1
  • unsigned 오른쪽 시프트 (>>>)
    short a = -1;
    var b = a >>> 1;
    var c = a >> 1;
    Console.WriteLine(b);
    Console.WriteLine(c);
2147483647
-1
  • UTF8 문자열 리터럴
    var s1 = "테스트"u8;

    byte[] s2 = "테스트";

    Span<byte> s3 = "테스트";

    ReadOnlySpan<byte> s4 = "테스트";

    Print(s1);
    Print(s2);
    Print(s3.ToArray());
    Print(s4.ToArray());

    void Print(byte[] data)
    {
        var output = BitConverter.ToString(data);
        Console.WriteLine(output);
    }
ED-85-8C-EC-8A-A4-ED-8A-B8
ED-85-8C-EC-8A-A4-ED-8A-B8
ED-85-8C-EC-8A-A4-ED-8A-B8
ED-85-8C-EC-8A-A4-ED-8A-B8
  • ReadonlySpan<char> switch 분기
    ReadOnlySpan<char> span = "Test";

    switch (span)
    {
        case "Test":
            Console.WriteLine("Hit!");
            break;
        default:
            Console.WriteLine("No hit");
            break;
    }
Hit!
좋아요 1