从DTE访问属性信息
本文关键字:信息 属性 访问 DTE | 更新日期: 2023-09-27 18:26:24
我已经编码了如下内容:
[Attrib(typeof(MyCustomType))]
public class TargetType
{
// .....
}
我想使用EnvDTE
来获得对typeof
所引用的CodeElement
的引用。我知道如何获得对属性参数的引用,并且我可以使用Value
,但这给了我字符串typeof(MyCustomType)
。
如果我使用Value
,我必须分解字符串,然后尝试找到类型,如果有两个类型具有相同的名称但名称空间不同,这会变得棘手。
有更简单的方法吗?
有更简单的方法吗?
不,我不这么认为,至少对于<=VS2013,似乎CodeAttributeArgument
没有走得更远,这是一种耻辱。他们应该发布CodeAttributeArgument2
,其中Value
为CodeExpr
:''。。
如果您使用>=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 获取用特定属性装饰的所有方法