C# 5.0
기다리는 코드를 줄 세워 적을 수 있게 되었습니다.
무엇이 더해졌나
C# 5.0은 .NET Framework 4.5와 함께 2012년에 나왔습니다. 추가된 문법은 몇 되지 않지만, 그중 하나가 비동기 코드를 작성하는 방법을 통째로 바꿨습니다.
- async / await — 기다리는 코드를 위에서 아래로 작성합니다.
- 호출자 정보 특성 — 부른 쪽의 이름과 줄 번호를 컴파일러가 채웁니다.
- foreach 변수의 범위 — 반복마다 변수가 새로 생기도록 바뀌었습니다.
기다리는 코드를 위에서 아래로
오래 걸리는 일을 기다리는 동안 스레드를 붙잡고 있으면 그 스레드는 다른 일을 하지
못합니다. 그래서 "끝나면 이것을 하라" 를 넘기는 방식으로 작성했는데, 일이 이어질수록
중첩이 깊어져 순서를 알기 어려웠습니다. await 는 같은 일을
위에서 아래로 작성하게 합니다.
Task<string> LoadAsync() { return Client.GetStringAsync(url) .ContinueWith(t => t.Result.Trim()); }
async Task<string> LoadAsync() { var text = await Client.GetStringAsync(url); return text.Trim(); }
모양만 짧아진 것이 아닙니다. ContinueWith 로 이어 붙이면
예외가 AggregateException 에 싸여 오고 반복문 안에서
이어 붙이기가 어려웠는데, await 는
try / 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의 오래된 함정이 정리되었습니다.