C# 3.0 Preview – var 키워드와 익명 형식

C# 3.0에서 가장 충격적으로 보였던 부분이었지만 이내 정확한 규정을 보고 실체를 알게 된 다소 놀라운 언어 사양이었다. 하지만 var의 용법은 우리가 알던 스크립트 언어에서의 사용법과 별반 차이가 없다는 것은 분명하다.

스크립트 및 인터프리팅 언어의 경우 형식이 동적으로 변형되는 기술을 지원하기 위하여 형식을 고정할 필요도 없고 고정해서는 안되는 특성을 가지고 있다. 이러한 풍부함 덕분에 스크립트 언어는 상당수의 노가다를 면할 수 있도록 도와주고는 있지만 컴파일 언어와는 달리 있는 그대로를 받아들일 수 없기 때문에 분석하고 해석하는데에 지출되는 비용이 만만치 않다는 단점이 있다.

var는 플랫폼이나 사용 환경에 따라서 조금씩 의미가 달라져왔지만 일반적으로 형식이 결정되지 않은 어떤 임의의 변수를 뜻한다. 보통은 var에 담기는 변수의 형식은 런타임, 그러니까 프로그램을 실행하는 도중에 결정된다고 알려져있다. 그러나 C# 3.0은 이 관점을 조금 다르게 보고 있는데 런타임이 아닌 컴파일 타임에서 이것을 해결하려고 하고 있다.

아래의 코드를 컴파일하고 그 결과로 만들어진 닷넷 어셈블리를 Reflector나 ILDASM 등으로 쪼개어보면 정확히 결과를 알 수 있다.


var oBuffer = new System.Text.StringBuilder();
oBuffer += “Welcome “;
oBuffer += “to “;
oBuffer += “C# 3.0!”;
oBuffer += Environment.NewLine;

어차피 컴파일 타임에 가서 정확한 형식이 결정되기 때문에 런타임에 손해를 끼칠 걱정을 할 필요도 없다보니 var를 남발해도 괜찮지 않을까 싶기도 하지만 우리가 일상적으로 사용하는 프레임워크들 중에서 딱 한 곳만은 var의 사용을 자제해야할 것으로 생각되는 곳이 있다. 바로 네트워크 서버 관련 프로그래밍과 ASP .NET 관련 기술들이다. 미리 만들어진 어셈블리를 불러다 쓰는 것이야 자유이지만 var 키워드를 남발하는 코드를 컴파일하는 시간까지 Service Time에 포함되는 종전의 ASP .NET 사이트에서는 별로 현명하지 못한 선택일 수 있다.

var 키워드를 어떻게 사용하던간에 그것은 프로그래머 개인의 문제이지만 종전의 C# 1.0과 C# 2.0에는 이런 규칙이 없었으므로 기존 코드와의 호환성을 위해서라도 var 키워드는 지금부터 설명하는 LINQ와 관련된 익명 형식에서만 사용하는 것이 현명하리라 본다.

익명 형식이란 C# 2.0에서 소개되었던 익명 대리자와는 또 다른 것으로 훨씬 제약이 심하다. 익명 형식 자체의 메리트보다는 LINQ와의 연동 때 편리하게 사용할 수 있도록 해주는 지원 사격용 언어 규약이라고 할 수 있다. 용법은 다음과 같다.


var oData = new { /* 읽기 전용 프로퍼티 이름 / = / 현재 문맥에서 사용하는 변수, 함수 반환 값 */, … };
형식을 정할 수 없다는 것을 감안할 때 var 키워드가 당연히 필요하다. 하지만 object에 담아도 문제는 없지 않았을까? 구조적으로 본다면 틀린 말도 아니다. 하지만 var는 컴파일 타임때 완전한 형식을 결정해주는 수단이라고 하였는데 object를 사용한다면 실제 형식을 유추하기 위해서 너무 많은 오버 헤드를 런타임에서 수반하게 되어있다. 그러므로 var 키워드를 정말 올바르게 사용하고자 한다면 위와 같은 유형이 정답인 것이다.

그리고 그냥 프로퍼티 이름이 아니라 왜 읽기 전용일까? 이것이 바로 절제의 미학이고 정확한 언어 사양 선정이라고 본다. 익명 형식이라 해서 아무렇게나 선언할 수 있게 허용한다면 C#의 기본 틀이 완전히 무너지는 양상을 띄게 된다.

더 중요한 것은 코드 문맥에서 사용할 수 있는 함수의 반환값, 변수의 대입을 지원하는 것인데 상당 부분의 코드가 감추어져있지만 이것을 위와 같이 인라인 표기로 한 번에 표현할 수 있도록 한 것이다. 물론 컴파일을 했을 경우 코드의 크기가 늘어날 것을 걱정한다면 역시 바람직하지는 않다. 하지만 편의성의 관점에서 접근해본다면 그렇게까지 손해갈 일은 아닐 것이다.

자, 그러면 한 가지 수수께끼가 드러난다. 익명 형식을 가지고 제네릭을 사용할 수 있을까?

물론 이와 같이 끝난다면 사용할 수 없다. 하지만 우회적으로 LINQ를 사용하면 IEnumerable<T>에 포장된 컬렉션을 탐색할 수 있다. 여기까지가 익명 형식을 통한 제네릭 사용의 전부인 셈이다. 그리고 당연히 이렇게 사용하는 LINQ의 결과 집합도 형식은 알 수 없으므로 var에 담겨야 합당한 것이나 통상적으로 LINQ의 결과 집합은 IEnumerable<T> 인터페이스와 호환된다는 것을 상식적으로 알고 접근하는 것 뿐이다. 🙂

다음 아티클에서는 C# 3.0에서 좀 더 쓰기 쉽고 간략하게 바뀐 변형 문법들을 살펴보기로 한다.

C# 3.0의 var에 대한 오해를 풀다

이번 주말에는 C# 3.0에서 개인적으로 가장 불만이라고 생각했던 var에 대한 용법을 상세히 알아봤다. var라니. 처음 C# 3.0 스펙을 보고 “헉 -_-“하고 놀랐던 부분이다. 분명히 컴파일언어라고 생각했는데 이런식으로 Variant Type을 언어 스펙에 집어넣다니. 스크립트 언어도 아닌데 말이다.

하지만 내 예상과는 달리 var의 용법은 크게 다른 것이었다. var는 단지 System.Object의 객체를 만들어 그 안에 무언가를 집어넣고 꺼내온다는 Boxing과 Unboxing 작업을 최소화할 수 있는 또 다른 수단이었다. C# 2.0에서, System.ValueType에 대한 Boxing과 Unboxing을 최소화하기 위하여 소개한 것이 Nullable Type (System.Nullable<T>)이었다면, C# 3.0에서는 Compile-Time의 수준에서 동적으로 형식을 결정하도록 하는 것이 바로 var 키워드의 용법이었다. 다시 말해, Runtime에 대해서는 털끝하나 손해주는 일이 없다는 것이며 대신 그 댓가를 컴파일러가 지불하는 셈이다.

단독으로 사용하는 var가 어떤 관점이나 측면에서 유용하고 효과적인지 아직은 모르겠다. 하지만, var를 단독으로 쓰는 경우보다는 C# 3.0의 다른 기능들 (LINQ, Lambda Expression 등)의 보조를 맞추기 위하여 사용하는 것이 좀 더 적절하지는 않을까라는 결론을 유추할 수 있었다.