有没有更简单的方法来做到这一点?
不,我不这么认为,至少对于 <= 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
同时,您可以使用回溯解决方案,这不是很好,但它应该可以工作,还没有完全 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
它肯定会比使用 current 容易得多CodeModel
。