C# 13 후보 기능에 대해 살펴봅니다.

위 링크의 Working Set이 C# 13 후보 기능입니다. – 목록에 남아 있다가 테스트를 완료하지 못해 다음 버전으로 넘어가기도 합니다.

어느덧 확인이 가능한 기능이 있어서 슬로그를 시작합니다.

11개의 좋아요

C#은 여러 언어에서 벤치마킹 할 정도로 이미 syntax sugar에 대해서 상위에 속하는 언어죠. 오히려 최근 C# 12에 도입된 static interface 에 대해서 논란이 있을 정도로요.

최근에 많이 생각이 드는건, 결국 커뮤니티가 중요한 것 같아요. 이러한 문제들은 프로그래밍 기법으로 언제든지 풀 수 있는 문제들이라고 생각합니다. 언어로만 해결할 수 있는 문제 뿐만 아니라, 프로그래밍 기법들을 만들어 내는 라이브러리들의 부재를 잘 극복해내면 좋겠네요.

오히려 이런 다양한 기능들을 언어적 수준에서 제공하는게 나중에는 rust 처럼 진입장벽을 높이는 효과도 될 수 있고요. golang은 오히려 반대로 언어 수준의 지원을 최소화 하지만 커뮤니티가 잘 발달한 것과 같이요.

7개의 좋아요

이와 별개로 항상 최신 소식들을 전달해주 셔서 @dimohy 님께 감사의 말씀 전달드립니다~

8개의 좋아요

미리보기 기능을 사용하려면 csproj에 다음의 설정을 추가 해야 합니다.

<EnablePreviewFeatures>True</EnablePreviewFeatures>
5개의 좋아요

C# 11의 인터페이스의 정적 추상 메서드를 말씀하시는 거죠?

어떤 논란이 있었는지 궁금하네요!

5개의 좋아요

저도 비슷한 생각입니다. 저 조차도 최신 C# 언어 스펙은 IDE나 보조 도구가 제안해주는 내용을 보고 필요에 따라 조금만 선택해서 사용하는 경우가 많고, 실제로 잘 쓰는 언어 버전은 아무리 높게 쳐줘야 7.x 대 이상으로는 잘 안넘어가는 것 같습니다.

언어에 대한 agressive한 개발보다는 이제는 런타임 기능 강화, 특히 아직은 갈 길이 먼 NativeAOT 관련 기능 개발에 좀 더 신경써주면 좋겠다는 아쉬움이 항상 남는 것 같습니다. ㅎㅎ

6개의 좋아요

이스케이프 문자 Escape character 17.9p1 적용됨

이스케이프 문자를 \e로 사용할 수 있도록 합니다.

char escape_char = '\e';

Assert.IsTrue(escape_char == (char)0x1b, "...");
Assert.IsTrue(escape_char == '\u001b', "...");
Assert.IsTrue(escape_char == '\U0000001b', "...");
Assert.IsTrue(escape_char == '\x1b', "...");

이제 콘솔에서 다음 처럼 이스케이프 문자를 사용할 수 있습니다.

Console.WriteLine("This is a regular text");
Console.WriteLine("\e[1mThis is a bold text\e[0m");
Console.WriteLine("\e[2mThis is a dimmed text\e[0m");
Console.WriteLine("\e[3mThis is an italic text\e[0m");
Console.WriteLine("\e[4mThis is an underlined text\e[0m");
Console.WriteLine("\e[5mThis is a blinking text\e[0m");
Console.WriteLine("\e[6mThis is a fast blinking text\e[0m");
Console.WriteLine("\e[7mThis is an inverted text\e[0m");
Console.WriteLine("\e[8mThis is a hidden text\e[0m");
Console.WriteLine("\e[9mThis is a crossed-out text\e[0m");
Console.WriteLine("\e[21mThis is a double-underlined text\e[0m");
Console.WriteLine("\e[38;2;255;0;0mThis is a red text\e[0m");
Console.WriteLine("\e[48;2;0;255;0mThis is a green background\e[0m");
Console.WriteLine("\e[38;2;0;0;255;48;2;255;255;0mThis is a blue text with a yellow background\e[0m");

5개의 좋아요

오 드디어 params에 컬렉션을 사용할 수 있게 됩니다.

17.10p3에 머지 되므로 아직은 테스트해 볼 수는 없습니다.

그리고 이것이 힙 할당 없는 params을 지원하는지는 아직은 모르겠습니다. 별도의 기능으로 Params Span + Stackalloc any array type이 남아있기 때문입니다.

3개의 좋아요

Params Span + Stackalloc any array type이 닫혔습니다. Params Collections에서 구현되었음을 알 수 있네요. 힙 메모리 할당없는 params의 동작성은 17.10p3에서 확인 가능합니다. (현재는 17.10p2이라 아직 확인할 수는 없습니다.)

3개의 좋아요

iterator와 async 에서 ref 및 unsafe를 허용하는 것이 C# 13에 추가될 것으로 보입니다. 목록에도 추가가 되었네요!


6개의 좋아요

메서드 그룹의 자연 유형 개선 Method group natural type improvements 17.9p2 적용됨

메서드 그룹이란 동일한 메서드명을 가진 메서드들을 의미합니다. 다음의 두 링크를 통해 관련 개선을 살펴볼 수 있습니다.

2개의 좋아요

개체 이니셜라이저에서 암시적 인덱서 접근 (Implicit indexer access in object initializers) 17.9p3 적용됨

다음의 코드 처럼

var t = new T();
t[^1] = 1;

class T
{
    private readonly int[] _data = new int[10];

    public int Length => _data.Length;

    public int this[int index]
    {
        get => _data[index];
        set => _data[index] = value;
    }
}

Index로 받지 않아도 Length를 속성으로 반환하면 인덱스 접근이 가능했습니다.
하지만 개체 이니셜라이저를 통해서는 컴파일 오류가 발생했는데요,

image

17.9p3 부터 컴파일 오류 없이 사용 가능합니다.

3개의 좋아요

Lock 개체 Lock object 17.10p2 적용됨

이제 lock문에서 사용할 수 있는 개체는 object 뿐만 아니라 System.Threading.Lock 개체가 될 수 있습니다.

private Lock @lock = new Lock();
...
lock (@lock)
{
    ....
}

Lock을 사용하는 lock문은 Lock에서 제공하는 락 진입 및 해제를 그대로 사용하게 됩니다.

class MyDataStructure
{
    private readonly Lock _lock = new();

    void Foo()
    {
        lock (_lock)
        {
            // do something
        }
    }
}
class MyDataStructure
{
    private readonly Lock _lock = new();

    void Foo()
    {
        using (_lock.EnterLockScope())
        {
            // do something
        }
    }
}

System.Threading.Lock의 동작은 기존 lock문의 동작과 구현이 다르며 앞으로 좀 더 최적화 될 여지가 있습니다.

2개의 좋아요

Params 컬렉션 Params-collections 17.10p3 적용됨

드디어 params 컬렉션을 테스트 할 수 있게 되었습니다. 이제 다음의 코드는 정상적인 코드이며 잘 실행됩니다.

PrintReadOnlySpanParams(1, 2, 3, 4, 5);
PrintListParams(1, 2, 3, 4, 5);
PrintEnumerableParams(1, 2, 3, 4, 5);


void PrintReadOnlySpanParams(params ReadOnlySpan<int> @params)
{
    foreach (var p in @params)
    {
        Console.WriteLine(p);
    }
}

void PrintListParams(params List<int> @params)
{
    foreach (var p in @params)
    {
        Console.WriteLine(p);
    }
}

void PrintEnumerableParams(params IEnumerable<int> @params)
{
    foreach (var p in @params)
    {
        Console.WriteLine(p);
    }
}

params ReadOnlySpan<>을 이용하면 가변 인자를 넘기기 위해 불필요하게 힙 할당할 필요가 없어집니다.

6개의 좋아요

부분 속성 Partial properties 17.11p3 적용됨

이전에는 소스 생성기를 통해 속성을 생성하고 싶어도 부분 속성이 없었기 때문에 다음 처럼 해야 했습니다.

[NotifyPropertyChanged]
private string _userName;

생성되는 코드

public partial string UserName
{
    get => _userName;
    set
    {
        if (value != _userName)
        {
            _userName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
        }
    }
}

이제 부분 속성 (Partial properties) 에 의해 다음처럼 사용할 수 있습니다.

[NotifyPropertyChanged]
public partial string UserName { get; set; }

생성되는 코드

private string __generated_userName;

public partial string UserName
{
    get => __generated_userName;
    set
    {
        if (value != __generated_userName)
        {
            __generated_userName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
        }
    }
}
7개의 좋아요

닷넷: 2294. C# 13 - (6) iterator 또는 비동기 메서드에서 ref와 unsafe 사용을 부분적으로 허용 (sysnet.pe.kr)
https://www.sysnet.pe.kr/2/0/13710

4개의 좋아요

field 키워드는 이번에도 결국 반영되지 않았네요…

아니 이거 구현은 3~4년 전 부 터 진행되었던 것 같은데 반영이 오래 걸리네요…

3개의 좋아요

오버로드 해결 우선순위 Overload Resolution Priority

아래 코드를 실행하면 Array가 찍히는데요, OverloadResolutionPriority 특성을 부여해서 Span이 찍히도록 조절할 수 있습니다.

var d = new C1();
int[] arr = [1, 2, 3];
d.M(arr); // Prints "Span"

class C1
{
    public void M(ReadOnlySpan<int> s) => Console.WriteLine("Span");
    // Default overload resolution priority
    public void M(int[] a) => Console.WriteLine("Array");
}

| 출력

Array
using System.Runtime.CompilerServices;

var d = new C1();
int[] arr = [1, 2, 3];
d.M(arr); // Prints "Span"

class C1
{
    [OverloadResolutionPriority(1)]
    public void M(ReadOnlySpan<int> s) => Console.WriteLine("Span");
    // Default overload resolution priority
    public void M(int[] a) => Console.WriteLine("Array");
}

| 출력

Span

이는 오버로드 메소드의 호출 우선순위를 조정하고자 할 때 유용해 보입니다.

2개의 좋아요