2012-03-01 20 views
29

ho codificato qualcosa come il seguente:Accesso informazioni attributo dal DTE

[Attrib(typeof(MyCustomType))] 
public class TargetType 
{ 
    // ..... 
} 

voglio usare EnvDTE per ottenere un riferimento alla CodeElement riferimento il typeof. So come ottenere un riferimento all'argomento attributo, e posso usare Value, ma questo mi dà la stringa typeof(MyCustomType).

Se utilizzo Value, devo suddividere la stringa e quindi provare a trovare il tipo, che diventa peloso se ci sono due tipi con lo stesso nome ma spazi dei nomi diversi.

C'è un modo più semplice per farlo?

+4

quindi come si ottiene un riferimento all'argomento attributo? – Maslow

+3

Hai esaminato Roslyn? Dovrebbe offrire le funzionalità che stai cercando. – jessehouwing

+0

Hai controllato la proprietà FullName dell'attributo? –

risposta

3

C'è un modo più semplice per fare questo?

No, io non la penso così, atleast per un < = VS2013, sembra che il CodeAttributeArgument non va oltre, che è la vergogna. Avrebbero dovuto rilasciato CodeAttributeArgument2 che ha Value come CodeExpr: \ ..

Se si utilizza> = VS2014, è possibile ottenere l'accesso a Roslyn, e dovrebberodiventare più facile - non so esattamente come si può accedere a roslyn all'interno dell'estensione VS, attendere e vedere.

Al fine di ottenere gli attributi, è possibile utilizzare VS helper:

public List<CodeElement> GetAllCodeElementsOfType(
    CodeElements elements, 
    vsCMElement elementType, 

    bool includeExternalTypes) 
{ 
    var ret = new List<CodeElement>(); 

    foreach (CodeElement elem in elements) 
    { 
     // iterate all namespaces (even if they are external) 
     // > they might contain project code 
     if (elem.Kind == vsCMElement.vsCMElementNamespace) 
     { 
      ret.AddRange(
       GetAllCodeElementsOfType(
        ((CodeNamespace)elem).Members, 
        elementType, 
        includeExternalTypes)); 
     } 

     // if its not a namespace but external 
     // > ignore it 
     else if (elem.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal && !includeExternalTypes) 
      continue; 

     // if its from the project 
     // > check its members 
     else if (elem.IsCodeType) 
     { 
      ret.AddRange(
       GetAllCodeElementsOfType(
        ((CodeType)elem).Members, 
        elementType, 
        includeExternalTypes)); 
     } 

     if (elem.Kind == elementType) 
      ret.Add(elem); 
    } 
    return ret; 
} 

Fonte originale: https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude

In una frattempo, è possibile utilizzare una soluzione backtracking, questo non è bello, ma dovrebbe funzionare , non l'ho testato esattamente al 100%. L'idea di base è quella di iniziare a tracciare all'indietro dalla classe e tenere traccia di diversi namespace/usings che si trovano nel percorso di una classe. Questo cerca di simulare o meno quello che un vero e proprio compilatore farebbe, se sta andando a risolvere un tipo:

var solution = (Solution2) _applicationObject.Solution; 
var projects = solution.Projects; 
var activeProject = projects 
    .OfType<Project>() 
    .First(); 

// locate my class. 
var myClass = GetAllCodeElementsOfType(
    activeProject.CodeModel.CodeElements, 
    vsCMElement.vsCMElementClass, false) 
    .OfType<CodeClass2>() 
    .First(x => x.Name == "Program"); 

// locate my attribute on class. 
var mySpecialAttrib = myClass 
    .Attributes 
    .OfType<CodeAttribute2>() 
    .First(); 



var attributeArgument = mySpecialAttrib.Arguments 
    .OfType<CodeAttributeArgument>() 
    .First(); 

string myType = Regex.Replace(
    attributeArgument.Value, // typeof(MyType) 
    "^typeof.*\\((.*)\\)$", "$1"); // MyType*/ 

var codeNamespace = myClass.Namespace; 
var classNamespaces = new List<string>(); 

while (codeNamespace != null) 
{ 
    var codeNs = codeNamespace; 
    var namespaceName = codeNs.FullName; 

    var foundNamespaces = new List<string> {namespaceName}; 

    // generate namespaces from usings. 
    var @usings = codeNs.Children 
     .OfType<CodeImport>() 
     .Select(x => 
      new[] 
      { 
       x.Namespace, 
       namespaceName + "." + x.Namespace 
      }) 
     .SelectMany(x => x) 
     .ToList(); 

    foundNamespaces.AddRange(@usings); 

    // prepend all namespaces: 
    var extra = (
     from ns2 in classNamespaces 
     from ns1 in @usings 
     select ns1 + "." + ns2) 
     .ToList(); 

    classNamespaces.AddRange(foundNamespaces); 
    classNamespaces.AddRange(extra); 

    codeNamespace = codeNs.Parent as CodeNamespace; 
    if (codeNamespace == null) 
    { 
     var codeModel = codeNs.Parent as FileCodeModel2; 
     if (codeModel == null) return; 

     var elems = codeModel.CodeElements; 
     if (elems == null) continue; 

     var @extraUsings = elems 
      .OfType<CodeImport>() 
      .Select(x => x.Namespace); 

     classNamespaces.AddRange(@extraUsings); 
    } 
} 

// resolve to a type! 
var typeLocator = new EnvDTETypeLocator(); 
var resolvedType = classNamespaces.Select(type => 
     typeLocator.FindTypeExactMatch(activeProject, type + "." + myType)) 
    .FirstOrDefault(type => type != null); 

È necessario EnvDTETypeLocator troppo.

Per VS2015, un esempio di integrazione Roslyn può essere trovato da qui: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs

Sarà sicuramente essere molto più facile di quanto lo sia con la corrente CodeModel.

Problemi correlati