2013-01-03 2 views
10

리플렉션 사전 컴파일을 할 수있는 방법이 있습니까?디자인 타임 리플렉션

의도적으로 T4를 사용하여 특정 인터페이스를 구현하는 클래스를 기반으로 맞춤 코드를 작성합니다. 나는 리플렉션을 호출 할 수 있다는 것을 알고 있지만 컴파일하기 전에 T4 스크립트에서 추가 코드를 빠져 나오기를 원한다. 그렇지 않으면 코드를 두 번 컴파일해야한다. DLL을 생성하려면 한 번, 이전에 생성 된 DLL에 T4가 반영되도록 두 번해야한다. 추가적인 스캐 폴딩.

디자인 타임에 리플렉션 할 수있는 방법이 있습니까?

더 좋은 방법이 있나요?

+0

템플릿과 클래스가 같은 프로젝트에 있습니까? 나는 단지 짐작하고 있지만 다른 프로젝트에 있다면 순서를 더 잘 제어 할 수 있으므로 다른 컴파일 후에 컴파일 할 수 있습니다. – Kobi

+0

그래, 나도 그렇게 생각했지만, 빌드를 멈추고, t4를 실행하고, 일시 중지를 해제 할 수있는 방법이 없으면, 하나씩 프로젝트를 빌드해야합니다. – Alwyn

+1

템플릿 생성이 [빌드 프로세스의 코드 생성] (http://msdn.microsoft.com/en-us/library/ee847423.aspx)의 경우 빌드를 중지 할 필요가 없습니다. 나는 실제로 그것을 한 적이 없으며 템플릿 엔진이 어느 컨텍스트에서 실행되는지는 확실하지 않지만 작동 할 수있는 것처럼 보입니다. – Kobi

답변

21

Visual Studio Automation에서 제공하는 CodeModel을 기반으로 코드 사전 빌드를 생성하는 방법이 있습니다. 프로젝트 인터페이스에는 해당 프로젝트의 모든 모델 아티팩트 그래프가 들어있는 속성 "CodeModel"이 있습니다. 출력 코드를 생성하는 클래스, 인터페이스, 속성 등을 찾기 위해 트래버스 할 수 있습니다.

dandrejw는 이미 Tangible T4-Editor을 언급했다. 무료 템플릿 갤러리가 있습니다. 재사용 가능한 템플릿 인 "유형의 Visual Studio 자동화 도우미"가 있으므로 귀하의 경우에 매우 유용합니다. 이 템플릿을 사용하면 다음과 같이 문제를 해결할 수 있습니다.

이것은 INotifyPropertyChanged를 구현하는 모든 클래스를 탐지하는 t4 템플릿 내의 코드입니다.

<# 
    // get a reference to the project of this t4 template 
    var project = VisualStudioHelper.CurrentProject; 
    // get all class items from the code model 
    var allClasses = VisualStudioHelper.GetAllCodeElementsOfType(project.CodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false); 

    // iterate all classes 
    foreach(EnvDTE.CodeClass codeClass in allClasses) 
    { 
     // get all interfaces implemented by this class 
     var allInterfaces = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.ImplementedInterfaces, EnvDTE.vsCMElement.vsCMElementInterface, true); 
     if (allInterfaces.OfType<EnvDTE.CodeInterface>() 
         .Any(i => i.Name == "INotifyPropertyChanged")) 
     { 
      #>Render your code here<# 
     } 
    } 
#> 

코드 스 니펫에 "여기에 코드 렌더링"이라고 표시된 곳의 출력 코드를 입력하십시오.

+0

정말 멋져요! UR 도움을 주셔서 감사합니다 – Alwyn

+0

Visual Studio 없이는 어떻게 동작합니까? (MSBuild 만 사용하여 빌드 서버에서) 어떻게합니까? – julealgon

+0

이 코드가 Visual Studio 없이는 작동하지 않을 것입니다.하나는 Visual Studio가 실행 중일 때만 EnvDTE CodeModel에 액세스하여 템플릿 호스트를 제공합니다. 그러나 VS없이 EnvDTE에 액세스하기위한 논리를 인스턴스화하는 또 다른 방법을 발견하면이 코드를 작동시킬 수 있습니다 ... 죄송합니다. – Nico

0

내가 아는 유일한 방법은 일부 코드 구문 분석 기능을 사용하는 것입니다. 나는 그것을하는 방법을 내 머리 꼭대기에서 벗어나는 방법을 생각할 수 없다. .NET에는 그 일을 할 수있는 몇 가지 유틸리티가 있다고 확신합니다.

상황을 잘 모르지만 보통 코드를 읽는 대신 코드를 생성하는 데 사용되는 XML 파일 또는 일부 UML 다이어그램 (클래스 다이어그램)을 사용하면 코드를 읽는 대신 중앙 집중식 정보를 얻을 수 있습니다. . 이는 사소한 작업을 단순화하고 변경 작업을보다 쉽게 ​​만들어 주며 코드 생성을 통해 변경 사항을 유출시킵니다. Visual Studio 용 Tangible T4 도구를 살펴보십시오.

+0

Ugh - 추한 일 이겠지요 :(깔끔한 트릭을 원하고 있습니다.) – Alwyn

6

향후 T4 VisualStudioHelper 템플릿을 사용하려는 독자가 아닌 경우 현재 프로젝트의 모든 클래스를 열거하는 자체 포함 템플릿이 아래에 나와 있습니다. Visual Studio 2013에서 테스트되었으며 코드에 영감을 받았습니다. T4 Site

<#@ template debug="true" hostSpecific="true" #> 
<#@ output extension=".cs" #> 
<#@ Assembly Name="System.Core" #> 
<#@ assembly name="EnvDte" #> 
<#@ import namespace="System" #> 
<#@ import namespace="System.IO" #> 
<#@ import namespace="System.Diagnostics" #> 
<#@ import namespace="System.Linq" #> 
<#@ import namespace="System.Collections" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#  
    foreach(var ns in GetNamespaceElements()) 
    { 
    foreach(var cc in ns.Members.OfType<EnvDTE.CodeClass>()) 
    { 
#>Render your code here<# 
    } 
    } 
#> 

<#+ 
    public IEnumerable<EnvDTE.CodeNamespace> GetNamespaceElements() 
    { 
    var visualStudio = (this.Host as IServiceProvider).GetService(typeof(EnvDTE.DTE)) 
         as EnvDTE.DTE; 
    var project = visualStudio.Solution.FindProjectItem(this.Host.TemplateFile) 
        .ContainingProject as EnvDTE.Project; 

    var projItems = new List<EnvDTE.ProjectItem>(); 
    FillProjectItems(project.ProjectItems, projItems); 
    var names = new HashSet<string>(projItems 
     .Where(i => i.FileCodeModel != null) 
     .SelectMany(i => i.FileCodeModel.CodeElements.OfType<EnvDTE.CodeElement>()) 
     .Where(e => e.Kind == EnvDTE.vsCMElement.vsCMElementNamespace) 
     .Select(e => e.FullName)); 

    var codeNs = new List<EnvDTE.CodeNamespace>(); 
    FillCodeNamespaces(project.CodeModel.CodeElements.OfType<EnvDTE.CodeNamespace>(), codeNs); 

    return codeNs.Where(ns => names.Contains(ns.FullName)); 
    } 

    public void FillCodeNamespaces(IEnumerable<EnvDTE.CodeNamespace> parents, List<EnvDTE.CodeNamespace> all) 
    { 
    foreach (var parent in parents) 
    { 
     all.Add(parent); 
     FillCodeNamespaces(parent.Members.OfType<EnvDTE.CodeNamespace>(), all); 
    } 
    } 

    public void FillProjectItems(EnvDTE.ProjectItems items, List<EnvDTE.ProjectItem> ret) 
    { 
    if (items == null) return; 
    foreach(EnvDTE.ProjectItem item in items) 
    { 
     ret.Add(item); 
     FillProjectItems(item.ProjectItems, ret); 
    } 
    } 
#>