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

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

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

11 Likes

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

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

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

7 Likes

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

8 Likes

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

<EnablePreviewFeatures>True</EnablePreviewFeatures>
5 Likes

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

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

5 Likes

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

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

6 Likes

이스케이프 문자 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 Likes

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

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

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

3 Likes

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

3 Likes

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


6 Likes

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

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

2 Likes

개체 이니셜라이저에서 암시적 인덱서 접근 (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 Likes

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 Likes

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 Likes

부분 속성 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 Likes

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

4 Likes

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

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

3 Likes

오버로드 해결 우선순위 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 Likes