ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [.NET 8.0] C# 12 새로운 기능 정리
    .NET 2023. 11. 16. 01:45
    반응형

    2023년 11월 15일 .NET 8.0 이 정식 릴리즈 되었다. 그와 함께 C# 언어 버전도 12로 올라오게 되었는데, 어떤 점이 생겼는지 정리해보았다.

     

    1.기본생성자(Primary Constructors)

    지금까진 class 에서 생성자 함수를 선언할 때 아래와 같이 생성한다.

    public class AsIs
    {
        private string name;
    
        public AsIs(string name)
        {
            // 생성자 함수
            this.name = name;
        }
    
        public void PrintName()
        {
            Console.WriteLine(name);
        }
    }

     

    C# 12부터는 record 형식과 마찬가지로 별도 생성자함수 없이 클래스 옆에 바로 선언이 가능하다.

    public class Tobe(string name)
    {
        public void PrintName()
        {
            Console.WriteLine(name);
        }
    }

     

    코드가 짧아지지만 private 형식이며, readonly와 같이 생성자 시점 이후에는 편집불가하게 처리하고 싶다면 기존 방식대로 선언해야하는 제약이 있다.

     

    2. 컬렉션 식(Collection Expressions)

    C#에서 전통적인 배열 선언 및 생성은 아래와 같이 처리하곤 했다.

    var asisNums = new int[] { 1, 2, 3 };
    var asisChars = new char[] { 'a', 'b', 'c' };

     

    javascript나 python 에서 대괄호 사이에 연속된 값을 넣어 선언하는 것에 비하면 어딘가 군더더기가 많았다. 그걸 MS에서도 알았는지, C#12 부턴 두 언어처럼 [ ]를 사용하여 선언할 수 있다.

    int[] tobeNums = [1, 2, 3];
    char[] tobeChars = ['a', 'b', 'c'];

     

    또한 javascript의 확산연산자(spread syntax) 와 유사한 연산자 방식으로 배열을 합칠 수 있다.

    C# 8.0 의 범위연산자와 유사해보이는데, 범위연산자는 a[1..3] 처럼 전체 배열에서 요소를 분할하며, 동일한 배열 형식을 반환하는 반면, 해당 연산자는 배열 요소를 분해(확산)한다는 점에서 차이가 있다.
    공식문서에서도 spread 연산자라고 표현하므로, 별개로 보는게 맞다.
    int[] a = [1,2,3];
    int[] b = [4,5,6];
    int[] c = [..a, ..b];

     

    해당방식에는 약간 함정이 있는데, 유형을 var 타입으로 선언할 순 없다. 그래도 이전 보단 코드가 짧아졌으며, 타 언어와 선언방식이 비슷해져서 복합적인 언어를 사용하는 입장에선 환영할 만한 기능이다.

    (수렴진화 개이득)

     

    3. ref readonly 매개 변수

    기존 in 과 동일하나 개발툴에서 해당 값이 ref 형식으로 들어와 사용됨을 명시적으로 안내할 수 있다.

    만약 개발툴에 경고표기를 무시하게끔 설정 했다면 안나올 수 있다.

     

    아래 소스를 .NET8 이상 사용 가능한 visual studio에서 작성해보자. 글쓴 시점에선 visual studio code나 별도 개발툴에서 지원이 안되 차이점을 못느낄 수 있다.

    int num = 5;
    PrintIn(num); // 별도 표기 없음.
    PrintIn(in num);
    PrintRef(num); // CS9192 표기
    PrintRef(in num);
    PrintRef(ref num);
    
    void PrintIn(in int num)
    {
        // in 키워드를 사용하면 해당 변수주소에 할당된 값을 가져오는 것만 가능하다.
        Console.WriteLine($"{num}");
    }
    
    void PrintRef(ref readonly int num)
    {
        // in과 동일하게 해당 num 변수주소에 할당된 값을 가져오는 것만 가능하다.
        // 차이점은 메소드를 사용하는 영역에서 ref 키워드를 명세하게 끔 경고를 줄 수 있다.
        Console.WriteLine($"{num}");
    }

     

    이미지를 보면 PrintRef(num); 부분에서 밑줄이 그어진 것을 확인할 수 있다. 오픈소스 개발자, 협업등 다수의 사용자가 참조할 수 있는 기능을 구현할 때 in 보단, ref readonly를 사용함으로써, 해당 변수가 값이 아닌 참조가 전달됨을 명확하게 안내할 수 있게 해준다.

     

    4. 기본 람다 매개변수(Default Lambda Parameters)

    함수의 파라메터 기본값 설정과 동일하게, 람다식에도 기본값 설정이 가능해졌다.

    var lambda = (string name = "ddochea") => $"Hello, {name}!";
    Console.WriteLine(lambda());
    Console.WriteLine(lambda("world"));

     

    물론 제약사항도 동일하다. 기본값이 존재하는 파라메터를 먼저 선언할 수 없다.

    var lambda2 = (string name = "ddochea", int year) => $"Hello, {name}!"; // 불가. int year가 먼저 정의되어야 함

     

     

    5. 모든 형식의 별칭(Alias any type)

     using 을 이용해 배열, 튜플, 포인터 형식 등을 별칭으로 지정하여 사용할 수 있다. c++ typedef와 유사하다.

    using Profile = (string name, int age);
    using Todos = System.Collections.Generic.List<string>;
    
    var ddochea = new Profile("ddochea", 1);
    Profile ddochea2 = ("ddochea", 2);
    (string name, int age) ddochea3 = ddochea;
    
    Todos todos = ["Learn .NET 8.0", "Learn C# 12"];

     

    6. 인라인 배열(Inline Array)

    기존 fixed 및 unsafe 를 통해서만 힙메모리의 고정크기 버퍼를 사용하며, 프로젝트에 '안전하지 않은 코드' 허용이 필요하지만, C#12 부터 고정크기 버퍼를 안전한 코드에서도 사용 가능하다.

    public struct Buffer 
    {
        private int _element0;
    }
    
    public unsafe struct AsIs // 프로젝트 AllowUnsafeBlocks 체크 필요
    {
        private fixed int _elements[10];
        
    }

     

    7. 실험적 특성(Experimental Attribute)

    형식, 메소드, 어셈블리가 실험적인 코드임을 안내하기위한 어트리뷰트이다. 콘솔 프로젝트에서 아래와 같이 작성 후 실행을 시도하면 오류로 인해 실패처리 될 것이다.

    var test = new Test();
    test.TestMethod();
    
    public class Test
    {
        [Experimental("Experimental000")]
        public void TestMethod()
        {
            Console.WriteLine("Test!");
        }
    }

     

     

    그럼에도 사용이 필요하다면 #pragma warning disable 처리로 무시할 수 있다.

    var test = new Test();
    #pragma warning disable Experimental000 // 형식은 평가 목적으로 제공되며, 이후 업데이트에서 변경되거나 제거될 수 있습니다. 계속하려면 이 진단을 표시하지 않습니다.
    test.TestMethod();
    #pragma warning restore Experimental000 // 형식은 평가 목적으로 제공되며, 이후 업데이트에서 변경되거나 제거될 수 있습니다. 계속하려면 이 진단을 표시하지 않습니다.

    8. 인터셉터(Interceptors)

    인터셉터는 구현된 C# 코드 사이에 어떤 기능을 Injection 처리하는 기능을 제공한다. 2023.11.16 기준 아직 실험적기능이므로 정식 반영시점에 별도로 다룰 예정이다.

     

    마치며

    크로스플랫폼 닷넷이 출시된 이후 매해 메이저한 버전업데이트와 언어스펙을 제공하겠다는 MS의 약속은 올해도 지켜졌다. 제한적이지만 ASP.NET Core 에서도 NativeAOT를 다룰 수 있게 되었으며, 언어스펙도 인기있는 python, javascript의 syntex를 참조하여 발전하는게 느껴진다.

    현재 근무중인 곳에선 최신 닷넷의 매력적인 요소를 전혀 사용할 수 없어 한편으론 씁쓸하다.

    참조

    What's new in C# 12 - C# Guide - C# | Microsoft Learn

    안전하지 않은 코드, 데이터에 대한 포인터, 함수 포인터 - C# | Microsoft Learn

    반응형

    댓글

Designed by Tistory.