성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>설치된 .NET 버전에 민감한 코드를 포함하는 경우, 다중으로 어셈블리를 만들어야 할까요?</h1> <p> 제목이 어렵군요. ^^<br /> <br /> 시나리오를 예로 들어볼까요?<br /> <br /> 가령, A 회사는 .NET 2.0용 공통 라이브러리를 제작했는데 후에 .NET 4.0에 특화된 코드가 추가되어야 한다고 가정해보겠습니다. 그것 때문에 별도의 프로젝트를 하나 더 추가해서 .NET 2.0용의 코드를 공유하는 것도 부담이고, 기존 DLL을 .NET 2.0용과 .NET 4.0용으로 #if/#else/#endif 처리하여 2개의 DLL로 빌드해서 유지하는 것도 부담입니다.<br /> <br /> 그냥, 하나의 DLL에서 서비스를 할 수는 없는 걸까요?<br /> <br /> <hr style='width: 50%' /><br /> <br /> (약간은 억지스럽지만 ^^) 이제, 코드 수준으로 내려가서 살펴보겠습니다. <br /> <br /> 예를 들어, 기존 라이브러리에 다음과 같은 Utility 성격의 코드를 이미 포함하고 있는 상태에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Collections.Generic; using System.Text; using System.IO; namespace ClassLibrary2 { public class Utility { public static string GetFileContents(string filePath) { return File.ReadAllText(filePath); } } } </pre> <br /> .NET 4.0으로 오면서 해당 라이브러리를 사용하는 개발자로부터 다음과 같은 코드를 추가해 달라는 요청을 받았습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static string GetString(dynamic dynamicObject) { return dynamicObject.ToString(); } </pre> <br /> 일단 위와 같은 기능이 추가된 이상, 더 이상 .NET 2.0용으로 빌드할 수가 없고 "Target framework"을 ".NET Framewor 4"로 지정해야만 빌드가 가능합니다. 여기에 그치지 않고, 라이브러리가 .NET 4.0용으로 빌드되면 그것을 참조하는 프로젝트들 역시 "Target framework"을 그에 맞게 올려주어야만 Visual Studio에서 정상적으로 참조되는 문제가 발생합니다.<br /> <br /> 이쯤 되면, 라이브러리 개발팀은 .NET 버전에 따른 DLL을 별도로 배포하는 수밖에 없어보입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 이런 경우에 한 가지 고려해 볼 수 있는 방법이 바로 "동적 소스 코드 컴파일"입니다. 얼마 전에, Roslyn의 스크립트 기능을 알아봤었는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Roslyn 맛보기 - C# 소스 코드를 스크립트처럼 다루는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1153'>http://www.sysnet.pe.kr/2/0/1153</a> </pre> <br /> CSharpCodeProvider를 사용하면, Roslyn과 유사한 방식으로 처리하는 것이 가능합니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CSharpCodeProvider Class ; <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/microsoft.csharp.csharpcodeprovider'>https://docs.microsoft.com/en-us/dotnet/api/microsoft.csharp.csharpcodeprovider</a> </pre> <br /> 그럼, 위의 문제를 CSharpCodeProvider를 이용해서 한번 풀어볼까요? ^^<br /> <br /> 우선, Library 프로젝트 자체는 .NET 2.0용으로 빌드할 것이기 때문에 절대로 코드 내에서 직접적으로 .NET 4.0용 코드를 사용해서는 안됩니다. 따라서, .NET 4.0용 코드는 다음과 같이 동적으로 소스 코드 컴파일을 하여 빌드해 내면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters compilerParams = new CompilerParameters(); compilerParams.GenerateExecutable = false; compilerParams.GenerateInMemory = true; <span style='color: blue; font-weight: bold'>compilerParams.ReferencedAssemblies.Add(typeof(Utility).Assembly.Location); compilerParams.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); compilerParams.ReferencedAssemblies.Add("System.Core.dll");</span> string classText = "using ClassLibrary2; " + Environment.NewLine + "<span style='color: blue; font-weight: bold'>class CLR4Support : ICLR4Support</span>" + Environment.NewLine + "{" + Environment.NewLine + " <span style='color: blue; font-weight: bold'>public string GetString(object objContext)</span>" + Environment.NewLine + " {" + Environment.NewLine + " <span style='color: blue; font-weight: bold'>dynamic dynamicObject = objContext;</span>" + Environment.NewLine + " if (dynamicObject == null) return \"[None]\";" + Environment.NewLine + " return dynamicObject.ToString();" + Environment.NewLine + " }" + Environment.NewLine + "}"; CompilerResults cr = provider.CompileAssemblyFromSource(compilerParams, classText); if (cr.Errors.Count > 0) { Console.WriteLine("Errors building into {0}", cr.PathToAssembly); foreach (CompilerError ce in cr.Errors) { Console.WriteLine(" {0}", ce.ToString()); Console.WriteLine(); } } else { ...[성공적으로 빌드된 경우 처리]... } </pre> <br /> 중간에 보면, ICLR4Support라는 인터페이스를 두었죠. 그와 같이 인터페이스를 공통으로 사용하면 동적으로 빌드된 모듈과 연동하는 것이 매우 쉽습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Type clr4Type = cr.CompiledAssembly.GetType("CLR4Support"); ConstructorInfo ctorInfo = clr4Type.GetConstructor(Type.EmptyTypes); <span style='color: blue; font-weight: bold'>_clr4Object = ctorInfo.Invoke(new object[] { }) as ICLR4Support;</span> </pre> <br /> 결국, .NET 2.0 라이브러리에서는 이렇게 얻은 _clr4Object 인스턴스를 이용하여 .NET 4.0용 응용 프로그램에도 그에 맞는 서비스 함수를 작성할 수 있게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static string GetString(object obj) { if (_clr4Object != null) { return _clr4Object.GetString(obj); } return null; } </pre> <br /> 어떠세요? 위와 같은 방식을 적절하게 이용하시면 .NET 2.0/4.0 기능을 모두 포함하는 DLL 작성은 물론이고 C# 소스 코드를 스크립트처럼 사용하는 것도 가능합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마치기 전에, CSharpCodeProvider를 사용함에 있어 유의해야 할 사항이 하나 있습니다.<br /> <br /> 내부적으로 CSharpCodeProvider.CompileAssemblyFromSource 메서드는 csc.exe 파일을 직접 실행해서 모듈을 빌드해 냅니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /noconfig /fullpaths @"C:\Users\...[사용자 계정]...\AppData\Local\Temp\hkjchh1x.cmdline" </pre> <br /> 즉, 매번 빌드하도록 하는 것은 불합리한 점이 있으니 적절하게 Cache를 하는 것이 좋으며 가능한 빌드해야 할 소스 코드를 모아서 한 번에 빌드를 끝내는 것이 좋습니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=651&boardid=331301885'>첨부된 파일은 위의 코드를 포함한 예제 프로젝트</a>입니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1207
(왼쪽의 숫자를 입력해야 합니다.)