3

我正在编写一个源生成器,但正在努力获取传递给我的属性的构造函数的参数值。

我将以下内容注入编译:

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();
}

查看 ConstructorArguments 属性如何为空

我误解了这个 API 吗?我究竟做错了什么?

4

2 回答 2

3

AttributeClass 属性是一个 ErrorType,这意味着编译器实际上并不知道该类型是什么。当我们有一个类型的名称但不知道它是什么时,我们填写了一个假的“错误”类型;这使得某些功能的一些下游处理更容易。无论如何,弄清楚它来自哪里。您评论说“编译时没有错误或警告”,但无论您的 Roslyn 代码在何种环境中运行,您的编译都可能会出现错误。

于 2021-11-05T17:21:19.513 回答
3

是不可变的Compilation。当您为属性添加源代码时,您仍然有一个Compilation对该属性一无所知的对象。这导致了AttributeClass@ ErrorTypejason-malinowski 提到的那样。这对于这种源生成器非常有名,解决方案很简单。Compilation使用您注入的符号创建一个新符号,然后SemanticModel从新符号中获取一个Compilation

            // 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.CodeAnalysis3.9 包开始,体验变得更好,您现在可以添加属性Initialize而不是Execute

        public void Initialize(GeneratorInitializationContext context)
        {
            // Register the attribute source
            context.RegisterForPostInitialization((i) => i.AddSource("CmdrMethod.g.cs", attributeText));
            // .....
        }

然后,您可以直接使用您获得的编译Execute

请参阅自动通知示例

于 2021-11-11T14:32:34.253 回答