如何使用CSharp.SyntaxFactory为nameof正确指定Roslyn的SyntaxNodes?
解决Roslyn构建
nameof(Array.Length)语法节点的问题 你遇到的问题核心是:nameof不是普通的方法调用,Roslyn为它提供了专门的NameOfExpression语法节点类型,而你之前用InvocationExpression来模拟,导致编译器把它当成了一个不存在的方法名,自然会报错。
修正方案
把原来构建InvocationExpression的代码替换成NameOfExpression,具体步骤如下:
- 替换
InvocationExpression(IdentifierName("nameof"))为NameOfExpression() - 给
NameOfExpression传入目标表达式(也就是Array.Length的成员访问表达式)
修正后的字段声明代码
FieldDeclaration( VariableDeclaration(PredefinedType(Token(SyntaxKind.StringKeyword))) .WithVariables( SingletonSeparatedList( VariableDeclarator(Identifier("Bar")) .WithInitializer( EqualsValueClause( NameOfExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("Array"), IdentifierName("Length")))))))) .WithModifiers( TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.ConstKeyword)))
完整修正后的测试代码
替换你的Compile方法中的rootNode部分,完整代码如下:
[Fact] public void Compile() { var rootNode = CompilationUnit() .WithUsings(SingletonList(UsingDirective(IdentifierName("System")))) .WithMembers( SingletonList<MemberDeclarationSyntax>( NamespaceDeclaration(IdentifierName("CompilationTests")) .WithMembers( SingletonList<MemberDeclarationSyntax>( ClassDeclaration("Foo") .WithMembers( SingletonList<MemberDeclarationSyntax>( // 这里是修正后的字段声明 FieldDeclaration( VariableDeclaration(PredefinedType(Token(SyntaxKind.StringKeyword))) .WithVariables( SingletonSeparatedList( VariableDeclarator(Identifier("Bar")) .WithInitializer( EqualsValueClause( NameOfExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("Array"), IdentifierName("Length")))))))) .WithModifiers( TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.ConstKeyword)))))))) .NormalizeWhitespace(); var compilation = CSharpCompilation.Create( "MyAssembly", new[] { CSharpSyntaxTree.Create(rootNode, CSharpParseOptions.Default, isGeneratedCode: true) }, new[] { MetadataReference.CreateFromFile(typeof(Array).Assembly.Location) }, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) ); using var memoryStream = new MemoryStream(); var result = compilation.Emit(memoryStream); if (!result.Diagnostics.IsEmpty) { foreach (var diagnostic in result.Diagnostics) _output.WriteLine(diagnostic.ToString()); _output.WriteLine(string.Empty); } _output.WriteLine(rootNode.ToString()); Assert.True(result.Success); }
为什么之前的方式不行?
Roslyn的语法模型严格区分语言特性对应的节点类型:
- 普通方法调用用
InvocationExpression nameof运算符作为C#的特殊语法,有自己的NameOfExpression节点
当你用InvocationExpression模拟时,编译器会把nameof解析为一个普通的标识符(方法名),而不是关键字,所以会报"nameof不存在"的错误;同时Array.Length在普通调用参数中会被当成实例成员访问,而nameof内部的表达式是不需要实例的,这也是第二个错误的原因。
修正后,Roslyn会正确识别nameof运算符,编译自然就能通过了。
内容的提问来源于stack exchange,提问作者feO2x
相关产品推荐
相关产品推荐

