2012-03-01 16 views
29

He codificado algo como lo siguiente:Acceso a información de atributos del DTE

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

Quiero utilizar EnvDTE para obtener una referencia a la CodeElement referencia el typeof. Sé cómo obtener una referencia al argumento del atributo, y puedo usar Value, pero eso me da la cadena typeof(MyCustomType).

Si uso Value, tengo que descomponer la cadena y luego tratar de encontrar el tipo, que se pone pesado si hay dos tipos con el mismo nombre pero diferentes espacios de nombres.

¿Hay alguna manera más fácil de hacerlo?

+4

Entonces, ¿cómo se obtiene una referencia al argumento de atributo? – Maslow

+3

¿Has mirado a Roslyn? Debería ofrecer las características que estás buscando. – jessehouwing

+0

¿Ha comprobado la propiedad FullName del atributo? –

Respuesta

3

¿Hay una manera más fácil de hacer esto?

No, no lo creo, al menos para un < = VS2013, parece que el CodeAttributeArgument no va más lejos, que es la vergüenza. Deberían haber lanzado CodeAttributeArgument2 que tiene como ValueCodeExpr: \ ..

Si utiliza> = VS2014, puede obtener acceso a Roslyn, y deben vuelto más fácil - no sé exactamente cómo se puede acceder a roslyn dentro de la extensión VS, tiene que esperar y ver.

el fin de obtener atributos, puede utilizar VS ayudante:

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; 
} 

Fuente original: https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude

En una por su parte, se podría utilizar la solución dando marcha atrás, esto no es agradable, pero debería funcionar , no lo he probado exactamente al 100%. La idea básica es comenzar a rastrear hacia atrás desde la clase y hacer un seguimiento de los diferentes espacios de nombres/usos que están en el camino de una clase. Este trata de simular más o menos lo que un verdadero compilador haría, si se va a resolver 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); 

Usted necesita EnvDTETypeLocator también.

Para VS2015, un ejemplo de integración Roslyn se puede encontrar desde aquí: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs

Definitivamente va a ser mucho más fácil de lo que es la actual CodeModel.

Cuestiones relacionadas