从DTE访问属性信息 [英] Accessing attribute info from DTE

查看:217
本文介绍了从DTE访问属性信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有codeD类似如下:

  [ATTRIB(typeof运算(MyCustomType))]
公共类的TargetType
{
  // .....
}

我想用 EnvDTE 来获得一个参考 codeElement 的<$ C引用$ C>的typeof 。我知道如何去属性参数的引用,我可以使用,但给我的字符串 typeof运算(MyCustomType)

如果我用,我必须打破字符串,然后试图找到类型,如果有两种名称相同,但不同的它获取毛茸茸命名空间。

有没有更简单的方法来做到这一点?


解决方案

  

有没有更简单的方法来做到这一点?


不,我不这么认为,ATLEAST为&LT; = VS2013,似乎 codeAttributeArgument 不会再往前走,这是耻辱。他们应该已经发布了 codeAttributeArgument2 codeExpr :\\ ..

如果您使用> = VS2014,你可以访问罗斯林,它的 成为容易 - 不知道究竟如何访问VS扩展中罗斯林,还得拭目以待。

为了获得属性,你可以使用VS帮手:

 公开名单&LT; codeElement&GT; GETALL codeElementsOfType(
    codeElements元素,
    vsCMElement元素类型,    布尔includeExternalTypes)
{
    VAR RET =新的List&LT; codeElement&GT;();    的foreach(codeElement中的元素ELEM)
    {
        //遍历所有名称空间(即使它们是外部)
        //&GT;它们可能包含项目code
        如果(elem.Kind == vsCMElement.vsCMElementNamespace)
        {
            ret.AddRange(
                GETALL codeElementsOfType(
                    ((codeNamespace)ELEM).Members,
                    元素类型,
                    includeExternalTypes));
        }        //如果它不是一个命名空间,但外部
        //&GT;忽略它
        否则,如果(elem.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal&放大器;&安培;!includeExternalTypes)
            继续;        //如果从项目的
        //&GT;检查其成员
        否则,如果(elem.Is codeTYPE)
        {
            ret.AddRange(
                GETALL codeElementsOfType(
                    ((codeTYPE)ELEM).Members,
                    元素类型,
                    includeExternalTypes));
        }        如果(elem.Kind ==元素类型)
            ret.Add(ELEM);
    }
    返回RET;
}

原文出处:<一href=\"https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/$c$cTemplates/VisualStudioAutomationHelper.ttinclude\" rel=\"nofollow\">https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/$c$cTemplates/VisualStudioAutomationHelper.ttinclude

在一同时,你可以使用回溯的解决方案,这是不是很好,但它应该工作,没有测试它正好是100%。其基本思路是开始从类向后跟踪和追踪不同的命名空间/ usings的arein类的路径。这将尝试模拟pretty多什么是真正的编译器会做,如果它要解决的类型:

  VAR解决方案=(Solution2)_applicationObject.Solution;
VAR项目= solution.Projects;
VAR activeProject =项目
    .OfType&LT;项目&GT;()
    。第一();//找到我的课。
VAR MyClass的= GETALL codeElementsOfType(
    activeProject。codeModel。codeElements,
    vsCMElement.vsCMElementClass,FALSE)
    .OfType&LT; codeClass2&GT;()
    。首先(X =&GT; x.Name ==计划);//找到我的阶级属性。
VAR mySpecialAttrib = MyClass的
    .Attributes
    .OfType&LT; codeAttribute2&GT;()
    。第一();VAR attributeArgument = mySpecialAttrib.Arguments
    .OfType&LT; codeAttributeArgument&GT;()
    。第一();字符串=的myType Regex.Replace(
    attributeArgument.Value,// typeof运算(的MyType)
    ^ * typeof运算\\\\((*)\\\\)$,$ 1); // MyType的* /变量codeNamespace = myClass.Namespace;
VAR classNamespaces =新的List&LT;串GT;();而(codeNamespace!= NULL)
{
    变量codeNS = codeNamespace;
    VAR namespaceName = codeNs.FullName;    VAR foundNamespaces =新的List&LT;串GT; {} namespaceName;    //生成usings命名空间。
    VAR @usings = codeNs.Children
        .OfType&LT; codeImport&GT;()
        。选择(X =&GT;
            新[]
            {
                x.Namespace,
                namespaceName +。 + x.Namespace
            })
        .SelectMany(X =&GT; x)的
        .ToList();    foundNamespaces.AddRange(@usings);    // prePEND所有命名空间:
    VAR额外=(
        从classNamespaces NS2
        从@usings NS1
        选择NS1 +。 + NS2)
        .ToList();    classNamespaces.AddRange(foundNamespaces);
    classNamespaces.AddRange(额外);    codeNamespace = codeNs.Parent为codeNamespace;
    如果(codeNamespace == NULL)
    {
        变量codeModel = codeNs.Parent为文件codeModel2;
        如果(codeModel == NULL)回报;        VAR elems = codeModel codeElements。
        如果(elems == NULL)继续;        VAR @extraUsings = elems
            .OfType&LT; codeImport&GT;()
            。选择(X =&GT; x.Namespace);        classNamespaces.AddRange(@extraUsings);
    }
}//决心类型!
VAR typeLocator =新EnvDTETypeLocator();
VAR resolvedType = classNamespaces.Select(类型=&GT;
        typeLocator.FindTypeExactMatch(activeProject,类型+。+的myType))
    .FirstOrDefault(类型=&GT;类型!= NULL);

您需要<一个href=\"http://domasmvc.google$c$c.com/svn/trunk/mvcscaffolding/T4Scaffolding/Core/ProjectTypeLocators/EnvDTETypeLocator.cs\"相对=nofollow> EnvDTETypeLocator 了。

对于VS2015 ,罗斯林一体化的例子可以从这里找到:的https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs

这肯定会是一个更容易比它是与当前的 codeModel

I have coded something like the following:

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

I want to use EnvDTE to get a reference to the CodeElement referenced by the typeof. I know how to get a reference to the attribute argument, and I can use Value, but that gives me the string typeof(MyCustomType).

If I use Value, I have to break down the string and then try to find the type, which gets hairy if there are two types with the same name but different namespaces.

Is there an easier way to do this?

解决方案

Is there an easier way to do this?

No, I don't think so, atleast for a <= VS2013, it seems that the CodeAttributeArgument doesn't go any further, which is shame. They should've released CodeAttributeArgument2 that has Value as CodeExpr :\..

If you use >=VS2014, you can get access to Roslyn, and it should become easier - don't know exactly how you can access roslyn inside VS extension, have to wait and see.

In order to get attributes, you can use 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;
}

Original source: https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude

In a meanwhile, you could use backtracking solution, this is not nice, but it should work, haven't tested it exactly 100%. The basic idea is to start tracking backwards from the class, and keep track of different namespaces/usings that arein the path of a class. This tries to simulate pretty much what a real compiler would do, if it's going to resolve a type:

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

You need EnvDTETypeLocator too.

For VS2015, an example of roslyn integration can be found from here: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs

It'll definitely be A lot easier than it is with current CodeModel.

这篇关于从DTE访问属性信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆