1. CodeDom과 Lambda 비교
|
CodeDom |
Lambda |
변수 선언 |
가능 |
불가 (단 하나의 표현식만 가능) |
함수 작성 |
사용자가 C# 문법을 알아야함 |
간단하게 식 사용 |
Math 및 기타 라이브러리 지원 |
지원 |
지원(lambda-parser 오픈소스 사용) |
컴파일 오류 |
오류 위치 확인 가능 |
오류 위치 확인 어려움 |
컴파일 형태 |
소스코드->CodeDom 그래프->IL 컴파일 (개발 복잡) 1. Dll 파일 형태 2. 메모리 형태 |
소스코드->Expression tree->IL 컴파일 (소스코드 관리 편함) 1. 메모리 형태 |
호출 속도 |
1. Func Delegate로 컴파일시 빠름, 단 함수 형태가 정의 되어있어야함 2. DynamicInvoke는 느리지만 유연함 |
1.1 변수 선언
Lambda는 단 하나의 표현식만 가능하다. 모든 클래스나 메소드, 또는 선언문은 위해 설계되지 않아서 변수 사용은 불가능하다.
- CodeDom
-Lambda
1.2 Math 및 기타 라이브러리 사용
-CodeDom
-Lambda
1.3 컴파일 오류
-CodeDom
-Lambda
1.4 컴파일
CodeDOM은 CodeDOM 그래프, Lambda는 익스프레션 트리 형태로 변환되어 컴파일 된다. 익스프레션 트리는 CodeDOM과 크게 다를 바가 없지만, 두가지 차이가 있다.
첫째, 익스프레션 트리는 단 하나의 표현식만 가능하다. 모든 클래스나 메소드, 또는 선언문은 위해 설계되지 않았다.
둘째, C#은 언어에서 람다 식과 마찬가지로 직접적으로 익스프레션 트리를 지원한다.
-CodeDom
Windows에서는 개별 어셈블리 언로드를 지원하지 않는다. 대신 프로그램 도메인을 언로드하면 어셈블리도 같이 언로드가 된다. 하나의 프로세스에서는 주 프로그램 도메인(1개)과 보조 프로그램 도메인(N개)을 만들 수 있다. 보조 프로그램 도메인을 하나 생성하여 그 도메인에 어셈블리를 로드하여 사용하고, 재 컴파일시 도메인을 언로드하고 다시 로드하는 방식으로 수행하면 프로그램을 재시작하지 않고 사용자 정의 함수를 호출 할 수 있다.
|
문제점은 도메인간에는 데이터가 공유되지 않기 때문에, MarshalByRefObject(응용 프로그램 도메인 간 경계를 넘어 개체에 엑세스할 수 있는 Abstract Class) Class를 상속 받은 개체에서만 사용자 정의 함수를 호출 할 수 있다. AppDomain으로 메소드 호출시에 Type이나 Assembly 등의 개체가 전달되거나 반환되면 상대방 AppDomain 측에서 해당 어셈블리의 로드가 자동으로 일어난다. 따라서 어셈블리의 동적 로드와 언로드의 목적 달성을 위해서는 이런 종류의 호출은 피해야 한다.
|
Code 저장 : 사용자가 작성한 Code를 저장하기 위해선 다른 Text파일로 저장 및 관리 해두는 방법과 Dll 파일의 EmbeddedResource 기능을 이용하는 방법이 있다.
컴파일시 ResourceWriter 개체를 이용해 Resource를 만들어서 사용자가 작성한 코드를 Resource 파일로 작성 후 Recource 파일을 어셈블리에 추가시키면 된다.
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters(); parameters.GenerateExecutable = false; parameters.IncludeDebugInformation = false; parameters.GenerateInMemory = true; System.Resources.ResourceWriter writer = new System.Resources.ResourceWriter("Resources.resx"); writer.AddResource("code.txt", Encoding.ASCII.GetBytes(code)); writer.Generate(); writer.Close(); parameters.EmbeddedResources.Add("Resources.resx"); parameters.ReferencedAssemblies.Add("System.dll"); parameters.ReferencedAssemblies.Add("System.Core.dll"); parameters.ReferencedAssemblies.Add("System.Data.dll"); parameters.ReferencedAssemblies.Add("System.Drawing.dll"); parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll"); parameters.OutputAssembly = DllName; CompilerResults r = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromSource(parameters, code); if (r.Errors.HasErrors == true) { CompilerError err = r.Errors[0]; MessageBox.Show(string.Format("Line:{0}, Column:{1}, ErrorNumber{2}\r\nMessage:{3}", err.Line.ToString(), err.Column.ToString(), err.ErrorNumber.ToString(), err.ErrorText)); } |
로드시에는 역으로 어셈블리에서 Resource를 검색 후 ResourceReader 개체를 이용해 Resource를 읽는다.
|
-Lambda
코드 형태의 문자열을 Lambda식으로 변경하는 기능은 .Net에서 제공해주지 않아서 직접구현하거나 오픈 소스를 이용해야한다. http://code.google.com/p/lambda-parser/ 오픈소스를 이용하면 응용 프로그램에 로드된 라이브러리는 모두 사용 할 수 있다. Parse 메서드는 String 형태의 Code와 파라미터의 타입 정보, 사용할 클래스의 NameSpace를 넘겨주면 Expression을 생성한다. 그 이후 Compile 메서드를 통해 Delegate를 생성한다.
|
1.5 CodeDom, Lambda 테스트
테스트 결과
컴파일 모드 : Release
테스트한 식 : (a * b) / 2 + 3 - 2 * 321
테스트 값 : float a = 2, float b = 3
호출 테스트 수 : 100,000번
총 테스트 수: 6회
CodeDom, Lambda 테스트 코드
Stopwatch sw = Stopwatch.StartNew(); float res = 0; for (int i = 0; i < 100000; i++) { if (param == null) { res = (float)this.dele.DynamicInvoke(null); } else { res = (float)this.dele.DynamicInvoke(param); } } sw.Stop(); Console.WriteLine(string.Format("DynamicInvoke : {0}", sw.Elapsed.ToString()), res.ToString()); sw = Stopwatch.StartNew(); for (int i = 0; i < 100000; i++) { res = this.func((float)param[0], (float)param[1]); } sw.Stop(); Console.WriteLine(string.Format("FuncInvoke : {0}", sw.Elapsed.ToString()), res.ToString()); sw = Stopwatch.StartNew(); for (int i = 0; i < 100000; i++) { res = ((float)param[0] * (float)param[1]) / 2 + 3 - 2 * 321; } sw.Stop(); Console.WriteLine(string.Format("Call : {0}\n", sw.Elapsed.ToString()), res.ToString()); |
CodeDom 결과 :
Dynamic |
Func |
CSharp | |
테스트1 |
0.0648687 |
0.0008836 |
0.0004886 |
테스트2 |
0.0615519 |
0.0008796 |
0.0004813 |
테스트3 |
0.0621032 |
0.0008879 |
0.0004833 |
테스트4 |
0.0618416 |
0.0008455 |
0.0004631 |
테스트5 |
0.0614814 |
0.0008925 |
0.0004909 |
테스트6 |
0.0619039 |
0.0008714 |
0.0004929 |
평균 |
0.062291783 |
0.00087675 |
0.00048335 |
Call 기준(배수) |
128.8751078 |
1.813902969 |
1 |
표 1 CodeDom 테스트 결과
Lambda 결과 :
Dynamic |
Func |
CSharp | |
테스트1 |
0.0962379 |
0.0009402 |
0.0004741 |
테스트2 |
0.0890706 |
0.000969 |
0.0005181 |
테스트3 |
0.07660805 |
0.0009647 |
0.0005141 |
테스트4 |
0.065423 |
0.0010326 |
0.0004853 |
테스트5 |
0.0898079 |
0.0009634 |
0.0004621 |
테스트6 |
0.0904247 |
0.00097 |
0.000487 |
평균 |
0.084595358 |
0.000973317 |
0.000490117 |
CSharp 기준(배수) |
172.6024926 |
1.985887714 |
1 |
표 2 Lambda 테스트 결과
1.6 결론
지금까지 CodeDom과 Lambda를 테스트 해보았다. 속도 테스트 결과 CodeDom이 약간 우세하였다. 내가 생각하는 장단점은 아래와 같다. 둘 중 하나를 선택하라면 Lambda식이 적합한 것 같다.
CodeDom의 장점은 변수의 선언 및 복잡한 식의 계산을 쉽게 할 수 있다는 것이다. 단점은 사용자가 C# 언어를 학습 해야 하고, 함수의 네이밍, 메모리 관리 등 개발이 복잡하다는 것이다. Lambda의 장점은 간편한 식을 작성 할 수 있고, 사용자에게 친숙하고, 함수의 관리가 편하다. 단점은 변수의 선언 및 복잡한 식은 설정하기 어렵다. |
프로젝트 : CalcDynamicCompile.zip
'.Net > Winform' 카테고리의 다른 글
C# TabControl 안에 윈도우 추가하기 (2) | 2014.08.19 |
---|---|
C# 멀티 헤더 DataGridView (0) | 2014.08.14 |
C# Math Expression Parse 오픈 소스 성능 비교 (0) | 2014.07.14 |
C# Excel 2007 추가기능(AddIn) 만들기 (0) | 2014.07.14 |
C# 마이크로(us) 단위의 Sleep 기능 구현 (0) | 2014.07.01 |