C# 11 κΈ°λŠ₯ 정리

.NET 7이 미리보기 λ¦΄λ¦¬μŠ€κ°€ μ’…λ£Œλ˜λ©΄μ„œ C# 11 κΈ°λŠ₯이 ν™•μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 이에 따라 C# 11 κΈ°λŠ₯을 λŒ€ν•΄ μ •λ¦¬ν•©λ‹ˆλ‹€.

C# 11 κΈ°λŠ₯ λͺ©λ‘

μƒˆλ‘­κ²Œ μΆ”κ°€λœ C# 11의 κΈ°λŠ₯은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

C# 11 κΈ°λŠ₯

파일 둜컬 νƒ€μž… (File-local Types)

파일 λ‚΄μ—μ„œλ§Œ μœ νš¨ν•œ νƒ€μž…μ„ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

// FileLocalType.cs
file class FileLocalType
{
}

// Program.cs
var s = new FileLocalType();
// 였λ₯˜	CS0246	'FileLocalType' ν˜•μ‹ λ˜λŠ” λ„€μž„μŠ€νŽ˜μ΄μŠ€ 이름을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. using μ§€μ‹œλ¬Έ λ˜λŠ” μ–΄μ…ˆλΈ”λ¦¬ μ°Έμ‘°κ°€ μžˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”.

ref ν•„λ“œ (ref fields)

Span<T>의 ref ν•„λ“œ _reference 처럼

 public readonly ref struct Span<T>
    {
        /// <summary>A byref or a native ptr.</summary>
        internal readonly ref T _reference;
        /// <summary>The number of elements this Span contains.</summary>
        private readonly int _length;
        ...

이제 일반 κ°œλ°œμžλ„ ref ν•„λ“œλ₯Ό λ§Œλ“€ 수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ref ν•„λ“œλŠ” μŠ€νƒ κ³΅κ°„μ—λ§Œ μ‘΄μž¬ν•  수 μžˆμœΌλ―€λ‘œ ref structμ—μ„œλ§Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ref struct Vector2D
{
	public ref int X;
	public ref int Y;
}

required 멀버 (Required members)

이제 required ν‚€μ›Œλ“œλ‘œ ν•„μˆ˜ 멀버λ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

public class User
{
    public required string Name { get; init; }
    public required int Age { get; init; }
}
var user1 = new User();
// 였λ₯˜	CS9035	ν•„μˆ˜ ꡬ성원 'User.Age'은(λŠ”) 개체 μ΄λ‹ˆμ…œλΌμ΄μ € λ˜λŠ” νŠΉμ„± μƒμ„±μžμ—μ„œ μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.
// 였λ₯˜	CS9035	ν•„μˆ˜ ꡬ성원 'User.Name'은(λŠ”) 개체 μ΄λ‹ˆμ…œλΌμ΄μ € λ˜λŠ” νŠΉμ„± μƒμ„±μžμ—μ„œ μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

var user2 = new User
{
	Name = "dimohy",
	Age = 45
};

정적 좔상 멀버 (DIM for Static Members)

이제 정적 속성 및 λ©”μ†Œλ“œ μ—­μ‹œ μΈν„°νŽ˜μ΄μŠ€λ‘œ κ΅¬ν˜„μ„ κ°•μ œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

public interface IValue<T>
{
    static abstract T Empty { get; }
}

public class NumValue : IValue<NumValue>
{
    public static NumValue Empty { get; } = new(0);

    private int _value;

    public NumValue(int value) => _value = value;
}

이λ₯Ό 톡해 이제 μ œλ„€λ¦­ ν˜•μ‹ μ œμ•½ 쑰건 whereλ₯Ό μ΄μš©ν•΄ 정적 속성 및 λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” κΈ°λŠ₯을 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

var emptyValue = GetEmptyValue(new NumValue(100));

T GetEmptyValue<T>(T value) where T : IValue<T>
{
    return T.Empty;
}

숫자 IntPtr (Numberic IntPtr)

IntPtr/UIntPtrκ³Ό ν”Œλž«νΌ 의쑴적인 μ •μˆ˜ νƒ€μž… nint / nuint이
ν”Œλž«νΌ 의쑴적인 λ™μΌν•œ μ •μˆ˜ν˜• 데이터 κ°’μž„μ—λ„ λΆˆκ΅¬ν•˜κ³  λ‹€λ₯΄κ²Œ μ·¨κΈ‰λ˜μ–΄ μ •μˆ˜ν˜•μ—μ„œ μ œκ³΅ν•˜λŠ” 연산을 μ œκ³΅ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

이제 두 νƒ€μž…μ€ λ™μΌν•œ νƒ€μž…μœΌλ‘œ μ²˜λ¦¬λ©λ‹ˆλ‹€. 즉, nint와 nuintλŠ” IntPtrκ³Ό UIntPtr의 alias이고 λ™μΌν•œ νƒ€μž…μ΄ λ©λ‹ˆλ‹€.

λΆ€ν˜Έ μ—†λŠ” 였λ₯Έμͺ½ μ‹œν”„νŠΈ μ—°μ‚°μž (Unsigned right shift operator)

Java에도 μ‘΄μž¬ν•˜λŠ” uinsiged right shift μ—°μ‚°μžλ₯Ό 이제 C#μ—μ„œλ„ μ œκ³΅ν•©λ‹ˆλ‹€.

int n = -2020987651; // 0b10000111100010100010110011111101

Console.WriteLine($"[n] \t\t {Convert.ToString(n, 2).PadLeft(32, '0')}");
Console.WriteLine($"[n] >>> 4 ==> \t {Convert.ToString(n >>> 4, 2).PadLeft(32, '0')}");
Console.WriteLine($"[n] >> 4 ==> \t {Convert.ToString(n >> 4, 2).PadLeft(32, '0')}");
[n]              10000111100010100010110011111101
[n] >>> 4 ==>    00001000011110001010001011001111
[n] >> 4 ==>     11111000011110001010001011001111

UTF8 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ (Utf8 String Literals)

이제 u8 접미사λ₯Ό 톡해 UTF8 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ„ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

ReadOnlySpan<byte> utf8Text = "λ””λͺ¨μ΄"u8; // λ˜λŠ”
var utf8Text = "λ””λͺ¨μ΄"u8;

UTF8 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ€ ReadOnlySpan<byte> νƒ€μž…μ΄λ―€λ‘œ 일반 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ 처럼 μƒμˆ˜λ‘œ μ·¨κΈ‰λ˜μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€. λ˜ν•œ ReadOnlySpan<byte> νƒ€μž…μ΄ μŠ€νƒμ—λ§Œ μ‘΄μž¬ν•  수 μžˆμœΌλ―€λ‘œ 비동기 λ©”μ„œλ“œμ—μ„œ μ‚¬μš©ν•  수 μ—†λŠ” μ œμ•½μ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€.

ReadOnlySpan<char>의 νŒ¨ν„΄ 맀칭 (Pattern matching on ReadOnlySpan<char>)

이제 νŒ¨ν„΄ λ§€μΉ­μ—μ„œ ReadOnlySpan<char>λ₯Ό λ¬Έμžμ—΄κ³Ό λ§€μΉ­μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

ReadOnlySpan<char> text = "λ””λͺ¨μ΄";

switch (text)
{
    case "λ””λͺ¨μ΄":
        break;
    default:
        break;
}

checked μ—°μ‚°μž (Checked Operators)

이제 overflow μ œμ–΄μ— ν•„μš”ν•œ checked/unchecked λ™μž‘μ„ 직접 μ²˜λ¦¬ν•  수 있게 λ©λ‹ˆλ‹€.

// 좜처 - μ„±νƒœμ˜ λ‹·λ„· 이야기
public static Int3 operator checked ++(Int3 lhs)
{
    if (lhs.value + 1 > 8388607)
    {
        throw new OverflowException((lhs.value + 1).ToString());
    }

    return new Int3(lhs.value + 1);
}

public static Int3 operator ++(Int3 lhs) => new Int3(lhs.value + 1);

μžλ™ κΈ°λ³Έ ꡬ쑰체 (auto-default structs)

이제 κ΅¬μ‘°μ²΄μ—μ„œ ν•„λ“œλ₯Ό μ΄ˆκΈ°ν™” ν•˜μ§€ μ•Šμ•„λ„ μžλ™μœΌλ‘œ default으둜 μ΄ˆκΈ°ν™” λ©λ‹ˆλ‹€.

// C# 11 μ΄μ „μ—λŠ” μ΄ˆκΈ°ν™” κ΄€λ ¨ 였λ₯˜ λ°œμƒ
public struct S
{
    public int x, y;
    public S()
    {
    }
}

λ³΄κ°„μ˜ κ°œν–‰ (Newlines in interpolations)

이제 λ³΄κ°„μ—μ„œ κ°œν–‰μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.

var v = $"Count is\t: { this.Is.A.Really()
                            .That.I.Should(
                                be + able)[
                                    to.Wrap()] }.";

λͺ©λ‘ νŒ¨ν„΄ (List patterns)

이제 λͺ©λ‘ νŒ¨ν„΄μ΄ μ§€μ›λ©λ‹ˆλ‹€.

int[] arr = { 1, 2, 3, 4, 5, 6 };

var result = arr switch
{
    //[1, .., 6] => 1,
    [1, .., 5, _] => 2,
    _ => 0
};
Console.WriteLine(result);

μ›μ‹œ λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ (Raw string literals)

이제 μ›μ‹œ λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

var rawText = """
    Line 1, value 1
    Line 2, value 2
    Line 3, value 3
    """;

Console.WriteLine(rawText);

λ§ˆμ§€λ§‰ λ‹«λŠ” """ μœ„μΉ˜μ— 맞게 λ¬Έμžμ—΄λ‘œ 잘 λŒ€μž…λ©λ‹ˆλ‹€.

Line 1, value 1
Line 2, value 2
Line 3, value 3

λ§Œμ•½ λ¬Έμžμ—΄ μ•ˆμ— """κ°€ ν¬ν•¨λ˜μ–΄ μžˆμ„ 경우, μ—΄κ³  λ‹«λŠ” ν°λ”°μ˜΄ν‘œλ₯Ό """"둜 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

var rawText = """"
    Line 1, value 1
    Line 2, value 2
    Line 3, value 3
    """Text"""
    """";

Console.WriteLine(rawText);
Line 1, value 1
Line 2, value 2
Line 3, value 3
"""Text"""

λ˜ν•œ λ¬Έμžμ—΄ 보간도 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

var num = 1;
var rawText = $""""
    Line 1, value {num}
    Line 2, value {num + 1}
    Line 3, value {num + 2}
    """Text"""
    """";

Console.WriteLine(rawText);
Line 1, value 1
Line 2, value 2
Line 3, value 3
"""Text"""

그런데 JSON λ¬Έμžμ—΄ 처럼 λ¬Έμžμ—΄μ— { λ˜λŠ” }이 μžˆμ„ 경우 $ λŒ€μ‹  $$을, {value} λŒ€μ‹  {{value}}으둜 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

var num = 1;
var rawText = $$""""
    {
    Line 1, value {{num}}
    Line 2, value {{num + 1}}
    Line 3, value {{num + 2}}
    }
    """Text"""
    """";

Console.WriteLine(rawText);
{
Line 1, value 1
Line 2, value 2
Line 3, value 3
}
"""Text"""

정적 λ©”μ†Œλ“œ 그룹에 λŒ€ν•œ μΊμ‹œ λŒ€λ¦¬μž (Cache delegates for static method group)

ν΄λ‘œμ €κ°€ μ—†λŠ” 정적 λ©”μ†Œλ“œ 그룹에 λŒ€ν•΄μ„œ C# 11 μ΄μ „μ—λŠ” ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ λŒ€λ¦¬μžμ˜ μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€.

void Demo(Func<string> action) { }
string GetString() => "";

```csharp
// C# code
Demo(GetString); // -> Demo(new Func<string>(GetString));

이제 ν΄λ‘œμ €κ°€ μ—†μœΌλ©΄ μ»΄νŒŒμΌλŸ¬λŠ” λŒ€λ¦¬μžλ₯Ό μΊμ‹œν•˜κ³  각 ν˜ΈμΆœμ—μ„œ μž¬μ‚¬μš© ν•©λ‹ˆλ‹€.

nameof(parameter)

이제 λ§€κ°œλ³€μˆ˜μ˜ 이름 μ—­μ‹œ nameof(parameter) ν˜•νƒœλ‘œ μ‚¬μš©ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Assert(1 == 1);
Assert("A" == "B");


void Assert(bool condition, [CallerArgumentExpression(nameof(condition))] string? message = default)
{
    Console.WriteLine(nameof(condition));
    Console.WriteLine(message);
}
condition
1 == 1
condition
"A" == "B"

μ‹œν”„νŠΈ μ—°μ‚°μžμ˜ μ œμ•½ μ™„ν™” (Relaxing Shift Operator)

이제 μ‹œν”„νŠΈ μ—°μ‚°μžμ˜ νƒ€μž… μ™„ν™”λ‘œ λ‹€λ₯Έ νƒ€μž…μ„ μ—°μ‚°μž λ§€κ°œλ³€μˆ˜λ‘œ 받을 수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

// 좜처 - μ„±νƒœμ˜ λ‹·λ„· 이야기 
C c = new C();
C d = c << 5.8f;

class C
{
    public static C operator << (C c, float o)
    {
        Console.WriteLine("shift operator called with " + o);

        return c;
    }
}

μ œλ„€λ¦­ νŠΉμ„± (Generic attributes)

이제 νŠΉμ„± νƒ€μž…μ— μ œλ„€λ¦­μ„ μ‚¬μš©ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

class ValueAttribute<TValue> : Attribute
    where TValue : struct
{
    public TValue V { get; set; }
}

[Value<int>(V = 10)]
class TestClass
{

}

참고 자료

μ’€ 더 μƒμ„Έν•œ λ‚΄μš©μ€ μ•„λž˜μ˜ λ¬Έμ„œλ₯Ό ν™•μΈν•˜μ„Έμš”.

12개의 μ’‹μ•„μš”

정말 ν•œ λˆˆμ— 보기 μ’‹κ²Œ 잘 μ •λ¦¬λ˜μ–΄μžˆλ„€μš”!!
덕뢄에 영문 λ¬Έμ„œλ₯Ό λ”°λ‘œ 찾아보지 μ•Šμ•„λ„ 될 것 κ°™μŠ΅λ‹ˆλ‹€.

μ΄λ²ˆμ—λ„ μ—­μ‹œλ‚˜ νŽΈλ¦¬ν•œ κΈ°λŠ₯듀이 많이 μΆ”κ°€λλ„€μš”
빼놓을 κΈ°λŠ₯듀이 μ—†μ§€λ§Œ, List patternsλž‘ Raw string literalsλŠ” 정말 많이 쓰일 것 κ°™λ„€μš”.
Abstract static memberλž‘ requiredλŠ” 금방 μ μ‘ν•˜κΈ΄ μ–΄λ €μšΈ 것 κ°™μŠ΅λ‹ˆλ‹€ γ…Žγ…Ž

2개의 μ’‹μ•„μš”
3개의 μ’‹μ•„μš”

λ¬Έμžμ—΄, 리슀트 νŒ¨ν„΄ 및 객체 생성을 μœ„ν•œ C# 11 κ°œμ„  사항

μ›μ‹œ λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄, 보간 λ¬Έμžμ—΄μ—μ„œ switch λ¬Έ, UTF8 λ¬Έμžμ—΄, 정적 λ©”μ„œλ“œ 좔상화λ₯Ό μ΄μš©ν•œ ꡬ문 뢄석 λ©”μ„œλ“œ, 리슀트의 νŒ¨ν„΄ 일치 κΈ°λŠ₯, required ν‚€μ›Œλ“œμ— λŒ€ν•œ μ†Œκ°œλ₯Ό ν•©λ‹ˆλ‹€.

3개의 μ’‹μ•„μš”