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.
Puede intentar usar una forma descrita Obtenga todos los métodos que están decorados con un atributo específico utilizando T4 / EnvDTE