C#

C# 기초부터 고급까지 Chapter 3.3. Expression Tree로 컴파일 타임 코드 생성

Juan_ 2025. 5. 5. 18:31
728x90

📘 Chapter 3.3: Expression Tree로 컴파일 타임 코드 생성


✅ 이 챕터에서 배울 것

  • Expression Tree란 무엇인가?
  • Func와 Expression<Func<T>> 차이
  • 동적으로 조건 쿼리 만들기
  • 메서드 호출도 Expression으로 표현 가능
  • 실전 예제 – 동적 필터링, 간단한 DSL
  • 리플렉션보다 빠른 이유와 주의점

1️⃣ Expression Tree란?

C# 코드를 트리 형태로 표현한 객체 구조

Expression<Func<int, int>> expr = x => x + 1;

이 코드는 실제로 이렇게 분해된다:

  • 파라미터 x
  • 연산자 +
  • 상수 1

→ 전부 트리 형태로 분해돼서 저장

📦 표현식 구조 살펴보기

Expression<Func<int, int>> expr = x => x + 1;

Console.WriteLine(expr.Body);           // (x + 1)
Console.WriteLine(expr.Parameters[0]);  // x

✅ 왜 쓰나?

용도설명
ORM쿼리 분석 후 SQL로 변환 (EntityFramework 등)
API 필터조건에 따라 쿼리 자동 생성
Rule Engine규칙을 코드로 저장 후 실행
코드 생성기조건에 따라 코드 만들어서 실행

2️⃣ Func vs Expression

Func<int, int> f = x => x + 1;
Expression<Func<int, int>> expr = x => x + 1;
항목FuncExpression
의미코드코드 + 트리
실행즉시 실행해석 또는 컴파일 후 실행
용도일반 로직ORM, DSL, 분석 등

→ Expression은 "코드의 형태로 존재"하고 분석 가능


3️⃣ Expression Tree 구성 직접 만들기

// x => x * 2 표현 직접 만들기
ParameterExpression param = Expression.Parameter(typeof(int), "x");
ConstantExpression two = Expression.Constant(2);
BinaryExpression body = Expression.Multiply(param, two);

var lambda = Expression.Lambda<Func<int, int>>(body, param);

Console.WriteLine(lambda); // x => (x * 2)
Console.WriteLine(lambda.Compile()(10)); // 20
➡️ 이렇게 직접 트리를 만들어서 런타임에 "코드를 조립" 가능

4️⃣ 실전: 조건식 필터링

public class Product
{
    public string Name { get; set; }
    public int Price { get; set; }
}

✅ "Price > 10000" 조건식 생성

ParameterExpression param = Expression.Parameter(typeof(Product), "p");
MemberExpression priceProp = Expression.Property(param, "Price");
ConstantExpression priceValue = Expression.Constant(10000);

BinaryExpression body = Expression.GreaterThan(priceProp, priceValue);
var lambda = Expression.Lambda<Func<Product, bool>>(body, param);
var list = new List<Product>
{
    new Product { Name = "노트북", Price = 12000 },
    new Product { Name = "연필", Price = 500 }
};

var filtered = list.Where(lambda.Compile()).ToList();
✅ 결과: "노트북"만 남는다!

5️⃣ 메서드 호출도 가능

MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });

ParameterExpression param = Expression.Parameter(typeof(Product), "p");
MemberExpression nameProp = Expression.Property(param, "Name");
ConstantExpression keyword = Expression.Constant("북");

MethodCallExpression body = Expression.Call(nameProp, containsMethod, keyword);

var lambda = Expression.Lambda<Func<Product, bool>>(body, param);
p => p.Name.Contains("북") 을 표현한 Expression

6️⃣ 리플렉션보다 빠른 이유

항목ReflectionExpression Tree
속성 호출Invoke 사용 → 느림미리 컴파일 → 빠름
메모리더 사용덜 사용
예외 발생런타임 많음비교적 안정적
➡️ Expression은 Compile() 후 Delegate 캐싱 가능 → 리플렉션보다 훨씬 빠른 대체재

✅ 정리 요약

항목요약
Expression Tree코드를 트리 형태로 표현, 조작 가능
Func vs Expression전자는 실행, 후자는 구조 분석용
실무 활용ORM, 조건 필터, DSL, 코드 생성
장점리플렉션보다 빠르고 안전
한계너무 복잡한 로직 구성은 어려움

📢 다음 챕터 예고 🎓

Chapter주제
3.4고급 LINQ 커스터마이징, 쿼리 최적화
728x90