c# visual-studio attributes envdte vs-extensibility

c# - Accediendo a la información del atributo del DTE



visual-studio attributes (2)

He codificado algo como lo siguiente:

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

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

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

¿Hay alguna forma más fácil de hacer esto?


¿Hay alguna forma más fácil de hacer esto?

No, no lo creo, al menos para un <= VS2013, parece que el CodeAttributeArgument no va más allá, lo que es una vergüenza. Deberían haber lanzado CodeAttributeArgument2 que tiene el Value como CodeExpr : / ..

Si usa> = VS2014, puede obtener acceso a Roslyn y debería ser más fácil. No sepa exactamente cómo puede acceder a Roslyn dentro de la extensión VS, tenga que esperar y ver.

Para obtener atributos, puedes usar 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; }

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

Mientras tanto, puede utilizar la solución de seguimiento, esto no es bueno, 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 se encuentran en la ruta de una clase. Esto intenta simular más o menos lo que haría un compilador real, 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 , se puede encontrar un ejemplo de integración de roslyn aquí: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs

Definitivamente será mucho más fácil de lo que es con CodeModel actual.