
빌 와그너, ⌜More Effective C# 2판⌟, 김완섭 옮김, 한빛미디어, 2019
이 포스팅은 ⌜More Effective C# 2판⌟의 내용을 기반으로 작성되었습니다.
C#에 LINQ와 동적 지원 기능이 추가되어 이전에 리플렉션 API를 사용할 때보다 더 좋은 표현식, 표현식 트리선택지가 생겼다.
커뮤니케이션 프레임워크에서 발생하는 문제와 같은 경우를 해결해준다.
var client = new ClientProxy<IService>();
var result = client.CallInterface<string>(srver => srver.DoWork(172));
ClientProxy<T>는 실제로 접근하는 서비스를 몰라도, 인수에 따라 적절한 메서드 호출로 연결해 준다.
CallInterface() 함수에서 사용한 MethodCallExpression은 모든 매개변수와 호출되는 메서드 이름을 이해하는데 필요한 정보를 모두 가지고 있다..
public TResult CallInterface<TResult>(Expression<Func<T, TResult>> op)
{
var exp = op.Body as MethodCallExpression;
var methodName = exp.Method.Name;
var allParameters = from element in exp.Arguments
select processArgument(element);
// 코드 생략
return default(TResult);
}
위의 호출자 코드를 아래와 같이 수정하여,
표현식 트리를 이용해서 사용자가 실행하고자 하는 코드를 런타임에 결정할 수 있다.
client.Callinterface(srver => srver.DoWork(
client.Callinterface(srv => srv.GetANumber())));
1번과 반대된다.
대규모 시스템에서는 원본 타입을 이용하여 대상타입의 객체를 생성하는 것과 연관된 문제를 해결해야 할 경우가 있다.
타입 변환 메서드를 만들면 타입의 속성의 이름이 모두 동일하고, 원본 객체에 public get, 대상 객체에 public set 접근자가 있어야 한다.
그 경우에 타입을 변환하는 메서드를 일일히 작성하는 대신, 필요한 구현체를 알아낼 수 있는 타입을 만드는 코드를 작성할 수 있다.
런타임에 코드를 생성해야 하는 작업을 가장 잘 다루는 것은 표현식을 생성해서 컴파일하고 실행하는 것이다.
표현식 트리를 만들기 위해 매개 변수를 선언한다.
var source = Expression.Parameter(typeof(TSource), "source");
그 다음 원본 객체의 속성을 대상에 할당한다.
List<Expression>을 만들어 문장을 구성하는 모든 표현식을 담은 배열을 만든다.
마지막으로, 지금까지 만든 모든 구문을 표현하는 람다 표현식을 생성한다.
var expr =
Expression.Lambda<Func<TSource, TDest>>(
Expression.Block(
new[] { dest }, // 표현식 매개변수
body.ToArray() //몸체
),
source // 람다 표현식
);
Block()은 모든 표현식을 담은 배열을 요구한다.
이후 코드 컴파일하여 호출 가능한 델리게이트로 변환한다.
var func = expr.Compile();
converter = func;
표현식 API는 두 가지 다른 방식으로 사용할 수 있다.
- 표현식을 매개변수로 취하는 메서드를 만드는 것
- 표현식을 파싱해서 해당 표현식의 개념을 코드로 생성할 수 있다.
- 런타임에 코드를 생성하는 것
- 코드를 작성하는 클래스를 생성, 실행하는 방법