C# 9.0 새 기능 알아보기
최근 유니티 2021 버전을 주로 사용하게 됨에 따라, C# 9.0의 기능을 정리할 필요성을 느껴 오랜만에 포스팅을 한다.
2022 버전에서도 C# 9.0을 사용하고 있으니 당분간 C# 신기능 포스팅은 이후로는 없을 듯 하다.
마이크로소프트 공식 문서를 참조했다.
레코드
레코드(record) 형식이 새로 추가되었다. 변경할 수 없는 데이터 모델을 지원하기 위해 만들어졌다.
레코드는 초기화 한 뒤에는 객체 내의 멤버들을 변경할 수 없다.
public record Person(string FirstName, string LastName);
public record Person
{
public string FirstName { get; init; } = default!;
public string LastName { get; init; } = default!;
};
위 코드는 Person이라는 record를 정의하는 방식을 보여준다.
속성의 set 자리에 난데없이 init이 들어가있는 모습을 볼 수 있는데, 이는 레코드의 특성 때문이다.
레코드는 최초에 초기화 한 뒤에는 객체의 내용을 변경할 수 없으므로, set 속성 대신 init 속성을 이용해야 한다.
- with 표현식
레코드가 불변이라면 일부만 변경하여 새 객체를 만들고 싶을 때 불편하지 않겠는가?
그럴 때에는 with를 사용하면 된다.
Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
Console.WriteLine(person1);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Person person2 = person1 with { FirstName = "John" };
Console.WriteLine(person2);
// output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 객체를 선언할 때 person1을 토대로 만들되, FirstName만 변경하도록 선언되어 있는 모습이다.
- 값 같음
레코드는 같은 값을 비교해야 할 상황이 있으면 편리하다.
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static void Main()
{
var phoneNumbers = new string[2];
Person person1 = new("Nancy", "Davolio", phoneNumbers);
Person person2 = new("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // output: True
}
Person에 Equals 등의 연산자를 재정의하지 않았는데도 완전 일치할 경우 True를 리턴하는 모습이다.
이제 데이터를 위한 클래스를 굳이 만들지 않더라도 데이터 비교가 가능해졌다. 변경은 불가능하지만.
- ToString()을 시전하면
Person { FirstName = Nancy, LastName = Davolio, ChildNames = System.String[] }
레코드 객체에 ToString()을 사용하면 속성 이름과 그 값을 알아서 보여준다.
여기서 ChildNames는 참조 형식인 string[] 타입이라 값 대신 표시되었다.
- 상속
레코드는 레코드에서 상속할 수 있다. 그러나 클래스와는 호환 불가능하다.
최상위 문
이제는 Main 메서드를 명시적으로 포함하지 않고도 작성이 가능하다... 라고는 하지만 유니티에는 영 쓸모가 없으니 패스한다.
패턴 일치 개선
패턴 일치가 또 개선됐다.
and와 or에 괄호를 사용하여 우선 순위를 명확하게 쓸 수 있고, 새로운 null 검사 구문을 쓸 수 있다.
public static bool IsLetterOrSeparator(this char c) =>
c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';
if (e is not null)
{
// ...
}
성능 및 interop
- 원시 크기 정수
nint, nuint라는 정수 형식이 새로 생겼다. nint는 native int, nuint는 unsigned native int이다.
unmanaged 코드나 저수준 라이브러리에서 사용하므로 일단 패스.
- 함수 포인터
delegate*를 사용하여 함수 포인터를 선언할 수 있다고 한다. C#에서 안전하지 않으므로 패스.
- localsinit 플래그 생략
SkipLocalsInit 속성을 추가하여 해당 객체의 로컬 변수를 0으로 초기화하는 걸 막을 수 있다.
마이크로 최적화급 용도이며, 위험하니 사용하지 않는 것이 맞을 듯. 패스.
new()
이제 만든 개체의 형식을 이미 알고 있으면 형식을 생략할 수 있다.
private List<WeatherObservation> _observations = new();
선언 단계에서 List<WeatherObservation>의 형식을 알고 있으므로, 이제는 굳이 객체 생성 시 써주지 않아도 된다.
무시 항목
이제 이름을 할당할 때 굳이 필요 없다면 작성하지 않아도 된다. 무슨 소리인지 모르겠으면 다음 코드를 보자.
(_, _, area) = city.GetCityInformation(cityName);
세 항목이 필요한 튜플을 작성하는데, 앞의 두 항목은 무시 항목인 _로 서술되어 있다.
이를 이용하면 코드 작성 시 area 항목만 필요하구나 하고 읽는이에게 명시적으로 전달할 수 있게 된다.
Console.WriteLine(obj switch
{
IFormatProvider fmt => $"{fmt.GetType()} object",
null => "A null object reference: Its use could result in a NullReferenceException",
_ => "Some object type without format information"
});
if (DateTime.TryParse(dateString, out _))
Console.WriteLine($"'{dateString}': valid");
위 코드와 같이 switch문에서의 default, TryParse의 out 부분 같은 건 필요 없다고 읽는이에게 명시적으로 전달할 수 있다.
코드 생성기 지원
컴파일 프로세스의 일부로 코드를 분석하고 새 소스 코드 파일을 작성(!)할 수 있다.
아니 이런 개꿀기능은 솔직히 글을 새로 파서 알아봐야 한다. 이 글에서는 패스.
'프로그래밍 > C#' 카테고리의 다른 글
왜 partial Enum은 없는 걸까? (0) | 2024.03.21 |
---|---|
ZString 라이브러리는 어떻게 제로 할당을 주장하는가 (0) | 2023.08.04 |
C# 8.0 새 기능 알아보기 (0) | 2022.09.20 |
C# 구조체 : 기본 매개변수를 사용한 생성자는 호출되지 않을 수도 있다 (0) | 2022.07.10 |
Enum.ToString 최적화하기 (0) | 2022.02.25 |
댓글
이 글 공유하기
다른 글
-
왜 partial Enum은 없는 걸까?
왜 partial Enum은 없는 걸까?
2024.03.21 -
ZString 라이브러리는 어떻게 제로 할당을 주장하는가
ZString 라이브러리는 어떻게 제로 할당을 주장하는가
2023.08.04 -
C# 8.0 새 기능 알아보기
C# 8.0 새 기능 알아보기
2022.09.20 -
C# 구조체 : 기본 매개변수를 사용한 생성자는 호출되지 않을 수도 있다
C# 구조체 : 기본 매개변수를 사용한 생성자는 호출되지 않을 수도 있다
2022.07.10