C# 런타임 코드 생성 (Emit, DynamicMethod)
왜 런타임 코드 생성인가
섹션 제목: “왜 런타임 코드 생성인가”직렬화, ORM, DI 컨테이너 등은 타입 정보를 보고 코드를 동적으로 생성해 리플렉션 호출보다 수십 배 빠른 성능을 냅니다.
DynamicMethod
섹션 제목: “DynamicMethod”IL을 직접 작성하는 방법입니다. 가장 낮은 수준의 접근입니다.
using System.Reflection.Emit;
// int Add(int a, int b) 메서드 동적 생성var method = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) });
ILGenerator il = method.GetILGenerator();il.Emit(OpCodes.Ldarg_0); // a 로드il.Emit(OpCodes.Ldarg_1); // b 로드il.Emit(OpCodes.Add); // 더하기il.Emit(OpCodes.Ret); // 반환
var add = (Func<int, int, int>)method.CreateDelegate(typeof(Func<int, int, int>));Console.WriteLine(add(3, 4)); // 7Expression Tree 컴파일 (권장)
섹션 제목: “Expression Tree 컴파일 (권장)”IL보다 훨씬 간결하고 가독성이 좋습니다.
// (int a, int b) => a + b 동적 생성var a = Expression.Parameter(typeof(int), "a");var b = Expression.Parameter(typeof(int), "b");var body = Expression.Add(a, b);var lambda = Expression.Lambda<Func<int, int, int>>(body, a, b);var compiled = lambda.Compile();
Console.WriteLine(compiled(3, 4)); // 7프로퍼티 getter 동적 생성 (리플렉션 대체)
섹션 제목: “프로퍼티 getter 동적 생성 (리플렉션 대체)”static Func<T, TValue> BuildGetter<T, TValue>(string propName) { var param = Expression.Parameter(typeof(T), "obj"); var prop = Expression.Property(param, propName); var cast = Expression.Convert(prop, typeof(TValue)); return Expression.Lambda<Func<T, TValue>>(cast, param).Compile();}
var getter = BuildGetter<User, string>("Name");// reflection 대비 ~100배 빠름 (캐싱 후)string name = getter(user);AssemblyBuilder로 타입 동적 생성
섹션 제목: “AssemblyBuilder로 타입 동적 생성”var assembly = AssemblyBuilder.DefineDynamicAssembly( new AssemblyName("DynamicAsm"), AssemblyBuilderAccess.Run);var module = assembly.DefineDynamicModule("DynamicMod");var typeBuilder = module.DefineType("MyClass", TypeAttributes.Public);
// 메서드 추가var methodBuilder = typeBuilder.DefineMethod("Hello", MethodAttributes.Public, typeof(string), Type.EmptyTypes);var il = methodBuilder.GetILGenerator();il.Emit(OpCodes.Ldstr, "Hello from dynamic type!");il.Emit(OpCodes.Ret);
Type dynamicType = typeBuilder.CreateType();object obj = Activator.CreateInstance(dynamicType)!;string result = (string)dynamicType.GetMethod("Hello")!.Invoke(obj, null)!;Roslyn Scripting API
섹션 제목: “Roslyn Scripting API”C# 소스 코드를 런타임에 컴파일하고 실행합니다. 가장 고수준 방법입니다.
using Microsoft.CodeAnalysis.CSharp.Scripting;
var result = await CSharpScript.EvaluateAsync<int>("1 + 2 + 3");Console.WriteLine(result); // 6
// 전역 변수 전달var globals = new ScriptGlobals { X = 10 };var script = CSharpScript.Create<int>("X * 2", globalsType: typeof(ScriptGlobals));var compiled = await script.RunAsync(globals);Console.WriteLine(compiled.ReturnValue); // 20
public class ScriptGlobals { public int X; }방법 비교
섹션 제목: “방법 비교”| 방법 | 학습 곡선 | 성능 | AOT 호환 |
|---|---|---|---|
DynamicMethod | 높음 (IL 지식 필요) | 최고 | ❌ |
Expression.Compile | 중간 | 높음 | ❌ |
AssemblyBuilder | 높음 | 최고 | ❌ |
Roslyn Scripting | 낮음 | 중간 (컴파일 비용) | ❌ |
Source Generator | 중간 | 최고 (빌드 타임) | ✅ |
- 단순 프로퍼티 getter/setter 동적 생성:
Expression.Compile+ 결과 캐싱 - 복잡한 IL 최적화:
DynamicMethod - 새 타입 동적 생성:
TypeBuilder - 유저 정의 스크립트 실행:
Roslyn Scripting - Native AOT 환경:
Source Generator로 빌드 타임 코드 생성