Доступ к информации об атрибутах из DTE

Я закодировал что-то вроде следующего:

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

Я хочу использовать EnvDTE , чтобы получить ссылку на CodeElement , на который ссылается typeof ]. Я знаю, как получить ссылку на аргумент атрибута, и могу использовать Value , но это дает мне строку typeof (MyCustomType) .

Если я использую Value , мне нужно разбить строку, а затем попытаться найти тип, что становится неприятным, если есть два типа с одинаковым именем, но с разными пространствами имен.

Есть ли более простой способ сделать это?

29
задан jessehouwing 26 December 2014 в 18:37
поделиться

2 ответа

Есть ли более простой способ сделать это?

Нет, я так не думаю, по крайней мере для < = VS2013, кажется, что 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.

4
ответ дан 28 November 2019 в 02:11
поделиться

Вы можете попробовать использовать описанный способ . Получить все методы, которые отмечены определенным атрибутом, используя T4 / EnvDTE

.
-2
ответ дан 28 November 2019 в 02:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: