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;
항목 | Func | Expression |
---|---|---|
의미 | 코드 | 코드 + 트리 |
실행 | 즉시 실행 | 해석 또는 컴파일 후 실행 |
용도 | 일반 로직 | 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️⃣ 리플렉션보다 빠른 이유
항목 | Reflection | Expression Tree |
---|---|---|
속성 호출 | Invoke 사용 → 느림 | 미리 컴파일 → 빠름 |
메모리 | 더 사용 | 덜 사용 |
예외 발생 | 런타임 많음 | 비교적 안정적 |
✅ 정리 요약
항목 | 요약 |
---|---|
Expression Tree | 코드를 트리 형태로 표현, 조작 가능 |
Func vs Expression | 전자는 실행, 후자는 구조 분석용 |
실무 활용 | ORM, 조건 필터, DSL, 코드 생성 |
장점 | 리플렉션보다 빠르고 안전 |
한계 | 너무 복잡한 로직 구성은 어려움 |
📢 다음 챕터 예고 🎓
Chapter | 주제 |
---|---|
3.4 | 고급 LINQ 커스터마이징, 쿼리 최적화 |
728x90
'C#' 카테고리의 다른 글
C# 기초부터 고급까지 Chapter 3.4. 고급 LINQ 커스터마이징 & 쿼리 최적화 (1) | 2025.05.06 |
---|---|
C# 기초부터 고급까지 Chapter 3.2. Reflection & Dynamic Type – 런타임 메타프로그래밍 (0) | 2025.05.05 |
C# 기초부터 고급까지 Chapter 3.1. Span , Memory , Unsafe – 퍼포먼스 챙기는 C# 코드 (0) | 2025.05.04 |
C# 기초부터 고급까지 Chapter 2.10. 프로젝트 구조 패턴 – Layered vs Clean Architecture 소개 (0) | 2025.05.04 |
C# 기초부터 고급까지 Chapter 2.9. 인터페이스 활용 – 다형성 실전 예제 (2) | 2025.05.03 |