SourceGenerator:属性 ConstructorArguments 为空
SourceGenerator: Attribute ConstructorArguments is empty
我正在编写一个源代码生成器,但正在努力获取传递给我的属性的构造函数的参数值。
我在编译中注入以下内容:
namespace Richiban.Cmdr
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CmdrMethod : System.Attribute
{
private readonly string _alias;
public CmdrMethod(string alias)
{
_alias = alias;
}
}
}
然后在我的示例应用程序中我有以下内容:
public static class InnerContainerClass
{
[CmdrMethod("test")]
public static void AnotherMethod(Data data)
{
Console.WriteLine($"In {nameof(AnotherMethod)}, {new { data }}");
}
}
这编译没有错误或警告,我成功地找到了所有用我的 CmdrMethod
属性装饰的方法,但我无法获得传递给属性的值,因为,出于某种原因,属性的ConstructorArguments
属性为空:
private static ImmutableArray<string> GetAttributeArguments(
IMethodSymbol methodSymbol,
string attributeName)
{
var attr = methodSymbol
.GetAttributes()
.Single(a => a.AttributeClass?.Name == attributeName);
var arguments = attr.ConstructorArguments;
if (methodSymbol.Name == "AnotherMethod")
Debugger.Launch();
return arguments.Select(a => a.ToString()).ToImmutableArray();
}
我是不是理解错了API?我做错了什么?
AttributeClass 属性 是一个 ErrorType,这意味着编译器实际上并不知道该类型是什么。当我们有类型的名称但不知道它是什么时,我们填写了一个假的“错误”类型;这使得某些功能的一些下游处理更容易。无论如何,弄清楚它是从哪里来的。您评论说“编译时没有错误或警告”,但无论您的 Roslyn 代码处于什么环境 运行,您的编译都可能会出错。
Compilation
是不可变的。当您为您的属性添加源代码时,您仍然有一个对属性一无所知的 Compilation
对象。正如@jason-malinowski 提到的,这导致 AttributeClass
成为 ErrorType
。这种源代码生成器非常有名,解决方案也很简单。用你注入的符号创建一个新的 Compilation
,然后从新的 Compilation
:
中得到一个 SemanticModel
// You should already have something similar to the following two lines.
SourceText attributeSourceText = SourceText.From("CmdrMethod source code here", Encoding.UTF8);
context.AddSource("CmdrMethod.g.cs" /* or whatever name you chose*/, attributeSourceText);
// This is the fix.
ParseOptions options = ((CSharpCompilation)context.Compilation).SyntaxTrees[0].Options;
SyntaxTree attributeTree = CSharpSyntaxTree.ParseText(atttributeSourceText, (CSharpParseOptions)options);
Compilation newCompilation = context.Compilation.AddSyntaxTrees(attributeTree);
// Get the semantic model from 'newCompilation'. It should have the information you need.
更新:从 Microsoft.CodeAnalysis
3.9 包开始体验变得更好,您现在可以在 Initialize
中添加属性而不是 Execute
:
public void Initialize(GeneratorInitializationContext context)
{
// Register the attribute source
context.RegisterForPostInitialization((i) => i.AddSource("CmdrMethod.g.cs", attributeText));
// .....
}
然后,您可以直接使用您在 Execute
中获得的编译。
我正在编写一个源代码生成器,但正在努力获取传递给我的属性的构造函数的参数值。
我在编译中注入以下内容:
namespace Richiban.Cmdr
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CmdrMethod : System.Attribute
{
private readonly string _alias;
public CmdrMethod(string alias)
{
_alias = alias;
}
}
}
然后在我的示例应用程序中我有以下内容:
public static class InnerContainerClass
{
[CmdrMethod("test")]
public static void AnotherMethod(Data data)
{
Console.WriteLine($"In {nameof(AnotherMethod)}, {new { data }}");
}
}
这编译没有错误或警告,我成功地找到了所有用我的 CmdrMethod
属性装饰的方法,但我无法获得传递给属性的值,因为,出于某种原因,属性的ConstructorArguments
属性为空:
private static ImmutableArray<string> GetAttributeArguments(
IMethodSymbol methodSymbol,
string attributeName)
{
var attr = methodSymbol
.GetAttributes()
.Single(a => a.AttributeClass?.Name == attributeName);
var arguments = attr.ConstructorArguments;
if (methodSymbol.Name == "AnotherMethod")
Debugger.Launch();
return arguments.Select(a => a.ToString()).ToImmutableArray();
}
我是不是理解错了API?我做错了什么?
AttributeClass 属性 是一个 ErrorType,这意味着编译器实际上并不知道该类型是什么。当我们有类型的名称但不知道它是什么时,我们填写了一个假的“错误”类型;这使得某些功能的一些下游处理更容易。无论如何,弄清楚它是从哪里来的。您评论说“编译时没有错误或警告”,但无论您的 Roslyn 代码处于什么环境 运行,您的编译都可能会出错。
Compilation
是不可变的。当您为您的属性添加源代码时,您仍然有一个对属性一无所知的 Compilation
对象。正如@jason-malinowski 提到的,这导致 AttributeClass
成为 ErrorType
。这种源代码生成器非常有名,解决方案也很简单。用你注入的符号创建一个新的 Compilation
,然后从新的 Compilation
:
SemanticModel
// You should already have something similar to the following two lines.
SourceText attributeSourceText = SourceText.From("CmdrMethod source code here", Encoding.UTF8);
context.AddSource("CmdrMethod.g.cs" /* or whatever name you chose*/, attributeSourceText);
// This is the fix.
ParseOptions options = ((CSharpCompilation)context.Compilation).SyntaxTrees[0].Options;
SyntaxTree attributeTree = CSharpSyntaxTree.ParseText(atttributeSourceText, (CSharpParseOptions)options);
Compilation newCompilation = context.Compilation.AddSyntaxTrees(attributeTree);
// Get the semantic model from 'newCompilation'. It should have the information you need.
更新:从 Microsoft.CodeAnalysis
3.9 包开始体验变得更好,您现在可以在 Initialize
中添加属性而不是 Execute
:
public void Initialize(GeneratorInitializationContext context)
{
// Register the attribute source
context.RegisterForPostInitialization((i) => i.AddSource("CmdrMethod.g.cs", attributeText));
// .....
}
然后,您可以直接使用您在 Execute
中获得的编译。