从DTE访问属性信息

本文关键字:信息 属性 访问 DTE | 更新日期: 2023-09-27 18:26:24

我已经编码了如下内容:

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

我想使用EnvDTE来获得对typeof所引用的CodeElement的引用。我知道如何获得对属性参数的引用,并且我可以使用Value,但这给了我字符串typeof(MyCustomType)

如果我使用Value,我必须分解字符串,然后尝试找到类型,如果有两个类型具有相同的名称但名称空间不同,这会变得棘手。

有更简单的方法吗?

从DTE访问属性信息

有更简单的方法吗?

不,我不这么认为,至少对于<=VS2013,似乎CodeAttributeArgument没有走得更远,这是一种耻辱。他们应该发布CodeAttributeArgument2,其中ValueCodeExpr:''。。

如果您使用>=VS2014,您可以访问Roslyn,并且应该变得更容易-不知道如何在VS扩展中访问Roslyn,只能拭目以待。

为了获取属性,您可以使用VS助手:

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

同时,你可以使用回溯解决方案,这并不好,但它应该有效,还没有完全测试过。基本思想是从类开始向后跟踪,并跟踪类路径中的不同名称空间/用法。这试图模拟一个真正的编译器会做什么,如果它要解决一个类型:

 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容易得多。

您可以尝试使用描述的方法使用T4/EnvDTE 获取用特定属性装饰的所有方法