[英]Get attribute arguments with roslyn
I try to get the named arguments for MyAttribute
with Roslyn.我尝试使用 Roslyn 获取MyAttribute
的命名参数。
var sourceCode = (@"
public class MyAttribute : Attribute
{
public string Test { get; set; }
}
[MyAttribute(Test = ""Hello"")]
public class MyClass { }
");
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { syntaxTree }, new[] { mscorlib });
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var syntaxRoot = syntaxTree.GetRoot();
var classNode = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>().Skip(1).First();
var classModel = (ITypeSymbol)semanticModel.GetDeclaredSymbol(classNode);
var firstAttribute = classModel.GetAttributes().First();
However firstAttribute.AttributeClass.Kind
equals to ErrorType
and consequently firstAttribute.NamedArguments
contains no elements.但是firstAttribute.AttributeClass.Kind
等于ErrorType
,因此firstAttribute.NamedArguments
包含任何元素。
The code isn't an anlyzer or something I have more complete context like a solution.代码不是分析器,也不是我有更完整上下文的东西,比如解决方案。
I can't see roslyn is missing any references or something else.我看不出 roslyn 缺少任何参考资料或其他内容。 What can I do to fully analyze the attribute?我该怎么做才能全面分析属性?
You need to fully qualify Attribute
type name: 您需要完全限定Attribute
类型名称:
var sourceCode = (@"
public class MyAttribute : System.Attribute // < here
{
public string Test { get; set; }
}
[MyAttribute(Test = ""Hello"")]
public class MyClass { }
");
Then it will work as you expect: 然后它将按预期工作:
var firstNamedArg = firstAttribute.NamedArguments[0];
var key = firstNamedArg.Key; // "Test"
var value = firstNamedArg.Value.Value; // "Hello"
Alternatively, you can add using System;
或者,您可以using System;
添加using System;
at the top: 在顶部:
var sourceCode = (@"
using System;
public class MyAttribute : Attribute
{
public string Test { get; set; }
}
[MyAttribute(Test = ""Hello"")]
public class MyClass { }
");
Instead of using the Roslyn's SemanticModel
you can also simply use the Syntax API to get the atttribute's argument info: 您可以使用语法API来获取atttribute的参数信息,而不是使用Roslyn的SemanticModel
:
var firstAttribute = classNode.AttributeLists.First().Attributes.First();
var attributeName = firstAttribute.Name.NormalizeWhitespace().ToFullString();
Console.WriteLine(attributeName);
// prints --> "MyAttribute"
var firstArgument = firstAttribute.ArgumentList.Arguments.First();
var argumentFullString = firstArgument.NormalizeWhitespace().ToFullString();
Console.WriteLine(argumentFullString);
// prints --> Test = "Hello"
var argumentName = firstArgument.NameEquals.Name.Identifier.ValueText;
Console.WriteLine(argumentName);
// prints --> Test
var argumentExpression = firstArgument.Expression.NormalizeWhitespace().ToFullString();
Console.WriteLine(argumentExpression);
// prints --> "Hello"
Once you have your xxxDeclaredSymbol
you can get all attributes like so一旦你有你的xxxDeclaredSymbol
你就可以得到所有的属性
var attributes = methodSymbol.GetAttributes().ToArray();
You can then loop over each AttributeData
in the array and use this extension I wrote, which will give you a list of all names + values passed to the attribute's constructor whether they were named or not...然后,您可以遍历数组中的每个AttributeData
并使用我编写的这个扩展,它将为您提供所有名称+ 传递给属性构造函数的值的列表,无论它们是否已命名...
// Used as a 3 value tuple for Name + TypeName + actual value
public class NameTypeAndValue
{
public string Name { get; private set; }
public string TypeFullName { get; private set; }
public object Value { get; private set; }
public NameTypeAndValue(string name, string typeFullName, object value)
{
Name = name;
TypeFullName = typeFullName;
Value = value;
}
}
public static class ITypeSymbolExtensions
{
// Converts names like `string` to `System.String`
public static string GetTypeFullName(this ITypeSymbol typeSymbol) =>
typeSymbol.SpecialType == SpecialType.None
? typeSymbol.ToDisplayString()
: typeSymbol.SpecialType.ToString().Replace("_", ".");
}
public static bool TryGetAttributeAndValues(
this AttributeData attributeData,
string attributeFullName,
SemanticModel model,
out IEnumerable<NameTypeAndValue> attributeValues)
{
var attributeValuesList = new List<NameTypeAndValue>();
var constructorParams = attributeData.AttributeConstructor.Parameters;
// Start with an indexed list of names for mandatory args
var argumentNames = constructorParams.Select(x => x.Name).ToArray();
var allArguments = attributeData.ConstructorArguments
// For unnamed args, we get the name from the array we just made
.Select((info, index) => new KeyValuePair<string, TypedConstant>(argumentNames[index], info))
// Then we use name + value from the named values
.Union(attributeData.NamedArguments.Select(x => new KeyValuePair<string, TypedConstant>(x.Key, x.Value)))
.Distinct();
foreach(var argument in allArguments)
{
attributeValuesList.Add(
new NameTypeAndValue(
name: argument.Key,
typeFullName: argument.Value.Type.GetTypeFullName(),
value: argument.Value.Value));
}
attributeValues = attributeValuesList.ToArray();
return true;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.