콘텐츠로 이동

C# 런타임 코드 생성 (Emit, DynamicMethod)

직렬화, ORM, DI 컨테이너 등은 타입 정보를 보고 코드를 동적으로 생성해 리플렉션 호출보다 수십 배 빠른 성능을 냅니다.

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)); // 7

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);
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)!;

C# 소스 코드를 런타임에 컴파일하고 실행합니다. 가장 고수준 방법입니다.

Microsoft.CodeAnalysis.CSharp.Scripting
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로 빌드 타임 코드 생성