Есть ли более простой способ сделать это?
Нет, я так не думаю, по крайней мере для <= VS2013, кажется, что <code>CodeAttributeArgument неиди дальше, это позор.Они должны были выпустить CodeAttributeArgument2
с Value
как CodeExpr
: \ ..
Если вы используете> = VS2014, вы можете получить доступ к Roslyn, и он должен станет проще - не знаю точно, как вы можете получить доступ к roslyn внутри расширения VS, придется подождать и посмотреть.
Чтобы получить атрибуты, вы можете использовать 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;
}
Исходный источник: https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude
В то же время, вы можете использовать решение для отслеживания, этоне хорошо, но это должно работать, не проверял это точно 100%.Основная идея состоит в том, чтобы начать отслеживание в обратном направлении от класса и отслеживать различные пространства имен / использования, которые находятся на пути класса.Он пытается симулировать то, что будет делать настоящий компилятор, если он собирается разрешить тип:
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);
Вам также необходимо EnvDTETypeLocator .
Для VS2015 пример интеграции Roslyn можно найти здесь: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs
Это определенно будет намного проще, чем с текущим CodeModel
.