본문 바로가기

프로그래밍 언어 노트/C#

[C#] Welcome To C# 9.0 [C# 9.0 변경사항 (예상)]

https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/

 

Welcome to C# 9.0 | .NET Blog

C# 9.0 is taking shape, and I’d like to share our thinking on some of the major features we’re adding to this next version of the language. With every new version of C# we strive for greater clarity and simplicity in common coding scenarios,

devblogs.microsoft.com

아마 Build 2020 에서 발표된 내용인듯 하다.

C#9.0의 경우 아직 릴리즈전이지만 추가예정인 주요기능을 살펴보자

 

Init-Only Properties

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

set 위치에 init 을 사용하여 선언시에만 초기화를 시킬수 있다.

public class Person
{
    private readonly string firstName;
    private readonly string lastName;
    
    public string FirstName 
    { 
        get => firstName; 
        init => firstName = (value ?? throw new ArgumentNullException(nameof(FirstName)));
    }
    public string LastName 
    { 
        get => lastName; 
        init => lastName = (value ?? throw new ArgumentNullException(nameof(LastName)));
    }
}

auto properties 가 아닌경우 위와같이 사용할수있다.

 

Record

위의 Init only 가 개별속성을 후속 할당 불가능으로 만든다면 Record는 개체 전체가 후속 할당 불가능이다.

public data class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

data 키워드를 이용하여 Record로 만든다.

-> 정식에서는 data 대신 record 로 명칭이 변경되었다

With-expressions

불변데이터에서 일부만 변경된 개체를 만들때 변경된요소만 제외하고 하나씩 복사하는것은 힘들다.

with 표현을 사용하여 일부데이터만 변경된 새로운 불변데이터를 얻을수있다.

var otherPerson = person with { LastName = "Hanselman" };

 

Value-based equality

레코드를 Equals 로 비교하면 Value 기반으로 비교한다. (구조체처럼 동작한다.)

즉 모든 속성들이 같은 값이면 같은 레코드 취급한다.

불변 데이터이므로 어찌보면 당연하다.

 

Data members

public data class Person { string FirstName; string LastName; } //1

public data class Person //2
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

1번 표현과 2번표현은 동일하다.

Record 의 모든 속성을 힘들게 init으로 처리할 필요가 없다.

 

Positional records

왜 positional 이라는 용어를 사용하는지는 모르겠지만 Destructing 이 가능하다.

public data class Person //1
{ 
    string FirstName; 
    string LastName; 
    public Person(string firstName, string lastName) 
      => (FirstName, LastName) = (firstName, lastName);
    public void Deconstruct(out string firstName, out string lastName) 
      => (firstName, lastName) = (FirstName, LastName);
}

public data class Person(string FirstName, string LastName); //2

1번 방식으로 구현해줄수있고

2번 방식으로 하면 자동으로 구현된다.

var person = new Person("Scott", "Hunter"); // positional construction
var (f, l) = person;                        // positional deconstruction

위와 같이 사용한다.

 

Records and mutation

제대로 해석한게 맞는지 모르겠는데 암튼 리코드는 Mutate 랑 안어울린다는 이야기인듯.

With-expressions and inheritance

public data class Person { string FirstName; string LastName; }
public data class Student : Person { int ID; }

Person person = new Student { FirstName = "Scott", LastName = "Hunter", ID = GetNewId() };
otherPerson = person with { LastName = "Hanselman" };

인경우 otherPerson 은 person 과 같이 Student 이며 ID는 동일하게 된다(GetNewID 로 새로운 ID를 할당받는것이 아님), C# Record의 Cloning 은 virtual 이라 자동적으로 파생클래스에서도 호출된다..?

을 뜻하는것 같은데 정확한 내용은 모르겠음. Mutate + 상속을 쓰지 말라는 의미인가..?

 

Value-based equality and inheritance

Person person1 = new Person { FirstName = "Scott", LastName = "Hunter" };
Person person2 = new Student { FirstName = "Scott", LastName = "Hunter", ID = GetNewId() };

위 와 비슷하게..

person1 입장 에서 person2는 같은 개체로 보일것이다. (자신이 가진 프로퍼티가 전부 같으므로)

근데 person2입장에서는 person1은 다른 개체이다.

따라서 두개는 Equals 하지않다.

C# Record 에서는 이를 자동적으로 Equals 하지않다고 판별해준다. (내부)

 

Top-level programs

using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}

using System;

Console.WriteLine("Hello World!");

처럼 코딩가능하다.

 

Improved pattern matching

패턴매칭이 더 좋아졌다.

public static decimal CalculateToll(object vehicle) =>
    vehicle switch
    {
       ...
       
        DeliveryTruck t when t.GrossWeightClass > 5000 => 10.00m + 5.00m,
        DeliveryTruck t when t.GrossWeightClass < 3000 => 10.00m - 2.00m,
        DeliveryTruck _ => 10.00m,

        _ => throw new ArgumentException("Not a known vehicle type", nameof(vehicle))
    };

위는 8.0의 패턴 매칭이다.

DeliveryTruck => 10.00m,

_ 를 뺄수있다. (Simple type patterns)

DeliveryTruck t when t.GrossWeightClass switch
{
    > 5000 => 10.00m + 5.00m,
    < 3000 => 10.00m - 2.00m,
    _ => 10.00m,
},

<, <= 과 같은 조건식을 패턴매치할수있다. 중첩 Switch 에서도 사용할수있다. (Relational patterns)

DeliveryTruck t when t.GrossWeightClass switch
{
    < 3000 => 10.00m - 2.00m,
    >= 3000 and <= 5000 => 10.00m,
    > 5000 => 10.00m + 5.00m,
},

and,or 도 쓸수있다.  (Logical patterns)

 

Improved target typing

Target-typed new expressions

Point p = new (3, 5);

new이후 타입명세 필요없음

Target typed ?? and ?:

Person person = student ?? customer; // Shared base type
int? result = b ? 0 : null; // nullable value type

?? 과 ?: 에서 같은 부모를가지는 타입 이지만 다른 파생클래스, nullable을 허용한다.

 

Covariant returns

abstract class Animal
{
    public abstract Food GetFood();
    ...
}
class Tiger : Animal
{
    public override Meat GetFood() => ...;
}

Meat 가 Food의 파생클래스일떄,

Food 가 리턴되는 함수에대한 공변셩을 가진다.

즉 위와같이 Animal 의  Food를 리턴하는 GetFood함수

Tiger(Animal 파생) 의 Meat(Food 파생) 을 리턴하는 함수로 오버라이딩이 가능하다.

728x90