我希望能够使用 Roslyn 代码分析从程序集中的 AssemblyInfo.cs 文件中读取一些程序集属性。
所以给出以下示例:
using System;
using System.Collections.Generic;
using System.Text;
[assembly: Helloworld.TestAttribute1("Test1")]
[assembly: Helloworld.TestAttribute1(TheValue = "Test1", IgnoreThis = "I dont want this one!")]
namespace Helloworld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class TestAttribute1 : Attribute
{
public TestAttribute1()
{
}
public TestAttribute1(string theValue)
{
this.TheValue = theValue;
}
public string TheValue { get; set; }
public string IgnoreThis { get; set; }
}
}
我希望能够提取类型 TestAttribute1
的属性和名为 TheValue
的已定义属性的值。
这在示例中定义了两次 - 第一次使用构造函数参数,另一个使用命名参数。
我有以下代码:
static void Main(string[] args)
{
string cs = GetFile();
SyntaxTree tree = CSharpSyntaxTree.ParseText(cs);
var root = (CompilationUnitSyntax)tree.GetRoot();
var compilation = CSharpCompilation.Create("").AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
// get the attributes
AttributeSyntax attr1 = root.DescendantNodes()
.OfType<AttributeSyntax>().ToArray()[0];
AttributeSyntax attr2 = root.DescendantNodes()
.OfType<AttributeSyntax>().ToArray()[1];
var ex1 = attr1.ArgumentList.Arguments.FirstOrDefault().Expression as LiteralExpressionSyntax;
var str1 = ex1.GetText().ToString();
var ex2 = attr2.ArgumentList.Arguments.FirstOrDefault().Expression as LiteralExpressionSyntax;
var str2 = ex2.GetText().ToString();
}
目前,我只是通过硬编码定位程序集属性来作弊。 再次对ArgumentList
进行硬编码以获得其中的第一个表达式。 这让我得到 str1 和 str2 的结果"Test1"
有没有办法只是说,给我类型TestAttribute1
的属性,然后说,给我名为TheValue
的属性的值?
实现,只需尝试从IAssemblySymbol
获取属性,这个符号可以从Compilation
中衍生:
var attribute = compilation.Assembly.GetAttributes().FirstOrDefault(x => x.AttributeClass.ToString() == "Helloworld.TestAttribute1");
if(!(attribute is null))
{
var ctorArgs = attribute.ConstructorArguments;
var propArgs = attribute.NamedArguments;
}
ctorArgs
和 propArgs
是TypedConstant
项的集合(propArgs
字典(,TypedConstant
具有属性Value
(如果是数组时Values
(,它将传递的值保留为 ctor 参数或属性值。最后,您只需要使用 TypedConstant.Type
过滤您感兴趣的参数。
这应如下所示:
SyntaxTree tree = CSharpSyntaxTree.ParseText(cs);
var root = (CompilationUnitSyntax)tree.GetRoot();
var compilation = CSharpCompilation.Create("test").AddSyntaxTrees(tree);
// get references to add
compilation = compilation.AddReferences(GetGlobalReferences());
var model = compilation.GetSemanticModel(tree);
var attrs = compilation.Assembly.GetAttributes().Where(x => x.AttributeClass.ToString() == "Helloworld.TestAttribute1");
foreach (var attr in attrs)
{
var ctorArgs = attr.ConstructorArguments;
var propArgs = attr.NamedArguments;
}
private static IEnumerable<MetadataReference> GetGlobalReferences()
{
var assemblies = new[]
{
typeof(System.Object).Assembly, //mscorlib
};
var refs = from a in assemblies
select MetadataReference.CreateFromFile(a.Location);
return refs.ToList();
}
<</div>
div class="one_answers"> 为了获取给定类型的属性,以下代码将起作用:
Func<AttributeSyntax, bool> findAttribute = (a) =>
{
var typeInfo = model.GetTypeInfo(a).ConvertedType;
return typeInfo.Name == "TestAttribute1" && typeInfo.ContainingNamespace.Name == "Helloworld";
};
AttributeSyntax[] attrs = root.DescendantNodes()
.OfType<AttributeSyntax>()
.Where(findAttribute)
.ToArray();