C# 6.0의 새로운 기능들

[제목] C# 6.0의 새로운 기능들

*주: 이 아티클은 2014년 6월에 작성된 글로서 C# 6.0에는 아래 기능들 중 삭제되거나 추가된 기능들이 있습니다. 최신 업데이트는 http://www.csharpstudy.com/CS6/CSharp-6-new-features.aspx 사이트에 정리되어 있습니다.

차기 버전인 C# 6.0 은 기존에 일상적으로 반복되었던 코드들을 보다 간결하게 해주는 편리한 기능들을 제공할 예정이다. C# 2.0의 Generics, C# 3.0의 LINQ과 같은 큼직 큼직한 Feature들을 제공하지는 않을 것으로 보이지만, 컴파일러 레벨의 문법적 개선을 통해 개발자가 보다 간결하게 코딩할 수 있도록 하는 기능들을 추가할 것 같다. 아직 Feature가 확정된 것이 아니고 계속 변경될 예정이지만, 지금까지 알려진 Feature를 간단히 정리해 본다.

1. Primary Constructor

C#에서 class나 struct를 정의할 때, 클래스명이나 구조체명 바로 다음에 파라미터를 전달하여 생성자로 활용하는 문법이다. 예를 들어, 아래 클래스 정의를 보면, class Person 뒤에 파라미터들이 붙은 것을 볼 수 있다. 기존에는 클래스내에 생성자를 만들었지만, C# 6.0에서는 이렇게 클래스명 뒤에 바로 파라미터들을 정의할 수 있는데, 이를 Primary Constructor라고 부른다.

//(1) Primary constructor
class Person(int id, string name, int age)
{
	//(2) Auto-Property Initializer
	public int Id { get; } = id;  
	public string Name { get; set; } = name;
	
	private int _age = age;
}

그러면 파라미터들은 클래스 내에 어떻게 저장되는가? 먼저 가장 쉽게는 3번째 파라미터 age처럼 이를 클래스 필드(_age)에 저장하는 것이다. 그리고 또 다른 방식으로 (아래 설명하듯이) 이를 프로퍼티에 저장하는 방식이 있다.

2. Auto-Property Initializer

Auto-Property는 C# 컴파일러가 Backing Field를 자동으로 만들어 주는 프로퍼티이다. 이 프로퍼티에 디폴트 초기값을 지정하는 기능이 C# 6.0 에 추가되었는데, 이를 Auto-Property Initializer라 부른다. 문법적으로는 위의 예제 Id, Name 에서와 같이 Auto-Property 정의 뒤에 = 을 붙이고 디폴트 값을 설정해 준다. 예제에서 Id 프로퍼티에는 Primary constructor의 첫번째 파라미터가 할당되고, Name에는 2번째 파라미터값이 할당되고 있다.

3. Dictionary initializer

기존에 Dictionary를 초기화하고 사용하는 방법은 다음과 같다. 초기화가 된 후에 Indexer 스타일([] 괄호 사용)로 사용한다.

var dic = new Dictionary<string,int> 
{
	{ "Lee", 1 }, 
	{ "Kim", 2 }
};
int x = dic["Lee"];

C# 6.0에서는 이 초기화과정에서 Indexer 스타일의 괄호 사용을 허용한다. 이는 해시테이블을 보다 직관적으로 초기화하는데 도움이 되는 것 같다.

// Dictionary initializer
var dic = new Dictionary<string, int>
{
	["Lee"] = 1, 
	["Kim"] = 2              
};			
int x = dic["Lee"];

4. Declaration expression

C#에서 out을 사용하기 위해서는 아래와 같이 사용 전에 미리 변수형을 선언해야 한다.

int i;
if (int.TryParse(s, out i)) {
}

C# 6.0에서는 이를 단순화하기 위해 out의 타입을 직접 뒤에 선언할 수 있게 하였다. 또한 out 변수형을 컴파일러가 추측해서 알아낼 수 있으면 그냥 var를 사용할 수 있게 하였다.

if (int.TryParse(s, out var i)) {
	Console.WriteLine(i);
}

5. Using static members

C#에서 static 메서드를 사용하기 위해서는 클래스명.메서드명 형식으로 클래스명을 앞에 지정해 준다.

using System;
Console.WriteLine(p.Name);

C# 6.0에서는 using에 클래스명을 써준다면, 해당 cs 파일 내에서 해당 클래스의 메서드를 직접 사용할 수 있게 하였다.

	
using System.Console;
WriteLine(p.Name);

6. catch/finally 블럭에서의 await 사용

C# 5.0에서 await 기능을 도입할 때, catch 나 finally 블럭에서 await를 사용하는 기능을 지원하지 않았다. 이 때문에 개발자는 Workaround를 사용해야 했었는데, 이를 6.0에서 Fix하여 지원하게 되었다.

//Await in catch/finally
IDbConnection conn;
try
{
	//...
	var response = await req.GetResponseAsync();
	//...
}
catch (Exception ex)
{
	await Log(ex);
}			
finally
{
	await Close(conn);
}

7. Exception Filter

C# 6.0에서 catch() 문 뒤에 추가적인 조건문을 사용할 수 있는데 이를 Exception Filter라 부른다. 아래 예제처럼, Win32Exception 에러가 발생했을 때, 이 Exception 객체의 내부 속성인 NativeErrorCode을 추가적으로 조사해서 그 값이 0x10 인 경우에만 catch 블럭이 실행하도록 하는 기능이다. VB.NET이나 F#에 이미 지원되는 기능으로 C#에서는 6.0에 추가된 기능이다.

// Exception Filter
try
{
}
catch(Win32Exception ex) if (ex.NativeErrorCode == 0x10)
{
}

8. Null propagation operator : ?.

C# 프로그래밍에서 NULL 체크만큼 많은 시간을 할애하는 곳도 아마 드물 것이다. 어떤 함수나 문장을 실행하고 결과가 NULL인지는 꼼꼼히 체크하는 것은 개발자에게는 Good Practice에 해당된다. 현재 C#에서 NULL 체크를 가장 Compact하게 한다면 아마 아래와 같이 될 것이다.

string s = GetA();
int? i = (s == null) ? null : s.Length;

C# 6.0에서는 ?. 라는 새로운 Operator를 도입하여 이를 다음과 같이 줄일 수 있다. 즉, s가 NULL인지 체크해서 NULL이면 결과에 NULL을 리턴하고 아니면 Length 속성을 리턴한다.

// Null propagation operator
string s = GetA();
int? n = s?.Length;

9. Binary Literal 과 Digit Separator

C#에서 Binary 값을 표현하기 위해서는 대부분 이를 Hex 값으로 변경하여 0xFE3F 과 같은 형식으로 사용한다. 이제 6.0 에서는 0b를 앞에 붙여 아래 예와 같이 순수한(?) 바이너리값 그대로 사용할 수 있다.

// Binary literal
int x = 0b01011111;

긴 숫자의 경우 중간에 Underline(_) 을 사용해서 숫자를 읽기 편하게 만들 수 있다. 이는 Ruby와 같은 다른 언어에 이미 존재하는 것으로 C# 6.0에 도입될 예정이다. 언더라인을 귿는 위치는 개발자가 임의로 지정할 수 있다. 즉, 매 숫자 2번째 자리마다 _ 을 그을 수도 있다.

// Digit Separator
long n = 1_234_567_890;

10. Expression-bodied member

C#에서 간단한 계산식에 의해 표현되는 속성을 생각해 보자. 아래와 같이 면적은 높이 x 넓이로 표현될 수 있다.

public int Area
{
	get 
	{
		return Height * Width;
	}
}

C# 6.0에선 이를 더 간결하게 다음과 같이 표현할 수 있다. 즉, Area는 속성이고, 산출식을 => 뒤에서 정의하고 있다.

public int Area => Height * Width;

Expression-bodied member는 또한 메서드에도 적용될 수 있는데, 아래 예는 Move라는 메서드가 새로운 위치의 Point객체를 리턴하는 코드를 보여주고 있다.

public Point Move(int a, int b) => new Point(X + a, Y + b);

11. Event Initializer

C#에서 클래스 객체를 생성하면서 동시에 속성에 값을 할당하기 위해서 다음과 같이 표현할 수 있다.

var c = new Class1 { Name = "A", Id = 1 };

이러한 기능의 연장선 상에서 C# 6.0에서는 이벤트 핸들러를 추가할 수 있게 되었다. 즉, 아래 예제처럼 OnClick 이벤트에 핸들러를 추가할 수 있다.

		
var btn = new Button { Name="B", OnClick += myHandler } 

12. Params IEnumerable

C#에서 가변적인 파라미터들을 받아들이기 위해서 params를 배열에 사용한다. 예를 들어, 아래 메서드는 0개부터 N개 까지의 임의의 파라미터들을 받아들일 수 있다.

void Run(params int[] vals) 
{
  foreach(int i in vals) { ... }
}

C# 6.0에서는 params를 배열 이외에 IEnumerable 에 적용할 수 있도록 하였는데, 이 기능은 특히 LINQ에서 유용할 수 있다.

void Run(params IEnumerable<int> vals) 
{
  foreach(int i in vals) { ... }
}

13. nameof operator

C# 6.0의 nameof 연산자는 Type이나 Method의 이름을 리턴하는 것으로 Logging 기능에 유용할 수 있다.

void Run() {
   Log(nameof(Run) + " : Error");
}

* C# 6.0은 현재 개발 단계에 있으며, 2014년 5월 말 현재 위에서 언급된 기능들 중 구현되지 않은 것들도 있다.
* 관련 링크 : https://www.facebook.com/csharpstudycom 에서 "C#의 미래 Roslyn" 참조
 



본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.