5.0 · 버전 이력

C# 5.0

기다리는 코드를 줄 세워 적을 수 있게 되었습니다.

2012 .NET Framework 4.5
한눈에

무엇이 더해졌나

C# 5.0은 .NET Framework 4.5와 함께 2012년에 나왔습니다. 추가된 문법은 몇 되지 않지만, 그중 하나가 비동기 코드를 작성하는 방법을 통째로 바꿨습니다.

  • async / await — 기다리는 코드를 위에서 아래로 작성합니다.
  • 호출자 정보 특성 — 부른 쪽의 이름과 줄 번호를 컴파일러가 채웁니다.
  • foreach 변수의 범위 — 반복마다 변수가 새로 생기도록 바뀌었습니다.
async / await

기다리는 코드를 위에서 아래로

오래 걸리는 일을 기다리는 동안 스레드를 붙잡고 있으면 그 스레드는 다른 일을 하지 못합니다. 그래서 "끝나면 이것을 하라" 를 넘기는 방식으로 작성했는데, 일이 이어질수록 중첩이 깊어져 순서를 알기 어려웠습니다. await 는 같은 일을 위에서 아래로 작성하게 합니다.

C# 4.0 까지
Task<string> LoadAsync() {
    return Client.GetStringAsync(url)
        .ContinueWith(t => t.Result.Trim());
}
C# 5.0 부터
async Task<string> LoadAsync() {
    var text = await Client.GetStringAsync(url);
    return text.Trim();
}

모양만 짧아진 것이 아닙니다. ContinueWith 로 이어 붙이면 예외가 AggregateException 에 싸여 오고 반복문 안에서 이어 붙이기가 어려웠는데, awaittry / catch 와 반복문을 그대로 사용합니다.

async Task LoadAllAsync(string[] urls) {
    foreach (var url in urls) {
        try { Console.WriteLine(await Client.GetStringAsync(url)); }
        catch (HttpRequestException ex) { Console.WriteLine(ex.Message); }
    }
}

C# 4.1 비동기 프로그래밍 에서 동작 원리와 주의할 점을 다룹니다.

호출자 정보 특성

부른 쪽을 컴파일러가 적어 줍니다

기록을 남길 때 어느 메서드에서 불렀는지를 함께 적으려면 그 이름을 직접 넘겨야 했습니다. 메서드 이름을 바꾸면 문자열도 함께 바꿔야 했고, 잊으면 틀린 이름이 남았습니다. 이제는 매개 변수에 특성을 붙여 두면 컴파일러가 부르는 자리에서 채웁니다.

using System.Runtime.CompilerServices;

void Log(string message,
    [CallerMemberName] string caller = "",
    [CallerLineNumber] int line = 0) {

    Console.WriteLine($"{caller}({line}): {message}");
}

부르는 쪽은 Log("기록합니다") 하나면 됩니다. 값을 넘기지 않은 매개 변수만 채우므로, 직접 넘기면 그 값이 그대로 쓰입니다. CallerFilePath 도 같은 방식입니다.

그 밖에

foreach 변수의 범위가 바뀌었습니다

이 판은 이미 동작하던 코드의 결과를 바꾼 드문 경우이기도 합니다. foreach 의 변수가 반복 전체에서 하나였기 때문에, 람다가 그것을 잡아 두면 반복이 끝난 뒤의 값을 보게 됐습니다. C# 5.0부터는 반복마다 변수가 새로 생깁니다.

var actions = new List<Action>();

foreach (var name in new[] { "홍길동", "김철수" })
    actions.Add(() => Console.Write(name + " "));

actions.ForEach(a => a());
// C# 4.0 까지 : 김철수 김철수
// C# 5.0 부터 : 홍길동 김철수

for 는 그대로입니다. 변수를 반복문이 직접 선언하므로 지금도 하나이고, 잡아 두면 마지막 값을 봅니다.

var fors = new List<Action>();

for (var i = 0; i < 2; i++)
    fors.Add(() => Console.Write(i + " "));

fors.ForEach(a => a());
// 2 2 — 지금도 그렇습니다

C# 3.5 람다식과 함수형 스타일 에서 잡아 두는 것이 무엇인지 다룹니다.

배경
  • 이 판이 겨눈 것은 기다리는 코드를 읽을 수 있게 만드는 것입니다.
  • await 는 문법만 바꾼 것이 아니라 예외와 반복문을 되찾아 주었습니다.
  • 같은 판에서 foreach 의 오래된 함정이 정리되었습니다.