C# 동적 컴파일
.NET에서 제공하는 컴파일 서비스를 이용하면 C#이나 VB.NET 코드 등을 컴파일해서 .EXE 나 .DLL 등을 동적으로 빌드할 수 있다. .NET의 CodeDomProvider 클래스는 이러한 동적 컴파일 (Dynamic Compilation) 기능을 제공하는데, CodeDomProvider.CreateProvider() 를 사용하여 해당 언어에 대한 컴파일러 객체를 생성하고, 필요한 컴파일 옵션(예를 들어 EXE를 만드는지 DLL을 만드는지 등)을 지정하게 된다. 실제 컴파일 빌드를 하는 메서드는 직접 문자열로부터 컴파일하는 CompileAssemblyFromSource(), 파일로부터 컴파일하는 CompileAssemblyFromFile(), 그리고 DOM 트리로부터 컴파일하는 CompileAssemblyFromDom() 등이 있다. 아래 예제는 C# 코드가 들어 있는 문자열로부터 컴파일 빌드하여 TEST.EXE라는 실행파일을 만드는 간단한 예이다.
예제
using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
namespace CSCompile
{
class Program
{
static void Main(string[] args)
{
// 컴파일할 코드
string code = @"
using System;
namespace TEST
{
class Program
{
static void Main(string[] args)
{
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += i;
}
Console.WriteLine(sum);
Console.ReadLine();
}
}
}
";
// C# 컴파일러 객체 생성
CodeDomProvider codeDom = CodeDomProvider.CreateProvider("CSharp");
// 컴파일러 파라미터 옵션 지정
CompilerParameters cparams = new CompilerParameters();
cparams.GenerateExecutable = true;
cparams.OutputAssembly = "TEST.EXE";
// 소스코드를 컴파일해서 EXE 생성
CompilerResults results = codeDom.CompileAssemblyFromSource(cparams, code);
// 컴파일 에러 있는 경우 표시
if (results.Errors.Count > 0)
{
foreach (var err in results.Errors)
{
Console.WriteLine(err.ToString());
}
return;
}
// (Optional) 테스트 실행
Process.Start("TEST.EXE");
}
}
}
using System.CodeDom.Compiler;
using System.Diagnostics;
namespace CSCompile
{
class Program
{
static void Main(string[] args)
{
// 컴파일할 코드
string code = @"
using System;
namespace TEST
{
class Program
{
static void Main(string[] args)
{
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += i;
}
Console.WriteLine(sum);
Console.ReadLine();
}
}
}
";
// C# 컴파일러 객체 생성
CodeDomProvider codeDom = CodeDomProvider.CreateProvider("CSharp");
// 컴파일러 파라미터 옵션 지정
CompilerParameters cparams = new CompilerParameters();
cparams.GenerateExecutable = true;
cparams.OutputAssembly = "TEST.EXE";
// 소스코드를 컴파일해서 EXE 생성
CompilerResults results = codeDom.CompileAssemblyFromSource(cparams, code);
// 컴파일 에러 있는 경우 표시
if (results.Errors.Count > 0)
{
foreach (var err in results.Errors)
{
Console.WriteLine(err.ToString());
}
return;
}
// (Optional) 테스트 실행
Process.Start("TEST.EXE");
}
}
}
C# 동적 클래스 생성과 사용 예제
문자열로부터 C# 클래스를 생성하여 이를 메모리 상에 생성하고, 이 클래스로부터 객체 인스턴스를 생성하고 그 속성과 메서드를 .NET Reflection을 이용하여 사용할 수 있다. 이것은 임의의 속성명과 메서드를 외부에서 정의하고 이를 읽어 클래스를 만들고 사용할 수 있다는 것을 의미한다. 아래 예제는 MyClass라는 클래스를 문자열로 정의하고 이를 컴파일하여 메모리상에 그 어셈블리를 만들고 있다. 이후 생성된 어셈블리로부터 MyClass 타입 객체를 생성하고 .NET Reflection으로 그 속성과 메서드를 호출하는 것을 예시하고 있다.
예제
using System;
using System.CodeDom.Compiler;
using System.Reflection;
namespace CSCompile
{
class Program
{
static void Main(string[] args)
{
string code = @"
using System;
namespace Test
{
class MyClass
{
public string Name { get; set; }
public void PrintName()
{
Console.WriteLine(Name);
}
}
}
";
// C# 컴파일러 객체 생성
CodeDomProvider codeDom = CodeDomProvider.CreateProvider("CSharp");
// 컴파일러 파라미터 옵션: 메모리에 어셈블리 생성
CompilerParameters cparams = new CompilerParameters();
cparams.GenerateInMemory = true;
// cparams.ReferencedAssemblies.Add("ThirdParty.dll");
// 소스코드를 컴파일해서 어셈블리 생성
CompilerResults results = codeDom.CompileAssemblyFromSource(cparams, code);
// 컴파일 에러 있는 경우 표시
if (results.Errors.Count > 0)
{
foreach (var err in results.Errors)
{
Console.WriteLine(err.ToString());
}
return;
}
// 어셈블리 로딩
Type myType = results.CompiledAssembly.GetType("Test.MyClass");
object myObject = Activator.CreateInstance(myType);
// 동적 클래스의 속성 읽기/쓰기
PropertyInfo pi = myObject.GetType().GetProperty("Name");
if (pi != null)
{
pi.SetValue(myObject, "Alex", null);
}
object name = pi.GetValue(myObject);
Console.WriteLine(name);
// 동적 클래스의 메서드 호출
MethodInfo mi = myObject.GetType().GetMethod("PrintName");
mi.Invoke(myObject, null);
}
}
}
using System.CodeDom.Compiler;
using System.Reflection;
namespace CSCompile
{
class Program
{
static void Main(string[] args)
{
string code = @"
using System;
namespace Test
{
class MyClass
{
public string Name { get; set; }
public void PrintName()
{
Console.WriteLine(Name);
}
}
}
";
// C# 컴파일러 객체 생성
CodeDomProvider codeDom = CodeDomProvider.CreateProvider("CSharp");
// 컴파일러 파라미터 옵션: 메모리에 어셈블리 생성
CompilerParameters cparams = new CompilerParameters();
cparams.GenerateInMemory = true;
// cparams.ReferencedAssemblies.Add("ThirdParty.dll");
// 소스코드를 컴파일해서 어셈블리 생성
CompilerResults results = codeDom.CompileAssemblyFromSource(cparams, code);
// 컴파일 에러 있는 경우 표시
if (results.Errors.Count > 0)
{
foreach (var err in results.Errors)
{
Console.WriteLine(err.ToString());
}
return;
}
// 어셈블리 로딩
Type myType = results.CompiledAssembly.GetType("Test.MyClass");
object myObject = Activator.CreateInstance(myType);
// 동적 클래스의 속성 읽기/쓰기
PropertyInfo pi = myObject.GetType().GetProperty("Name");
if (pi != null)
{
pi.SetValue(myObject, "Alex", null);
}
object name = pi.GetValue(myObject);
Console.WriteLine(name);
// 동적 클래스의 메서드 호출
MethodInfo mi = myObject.GetType().GetMethod("PrintName");
mi.Invoke(myObject, null);
}
}
}