代码之家  ›  专栏  ›  技术社区  ›  Erjot

Roslyn:SyntaxTree编译失败,而SyntaxTree编译的字符串有效

  •  0
  • Erjot  · 技术社区  · 3 年前

    [编辑]解决方案: 问题通过以下答案解决了。我使用了这部分代码:

    SyntaxFactory.IdentifierName(
        SyntaxFactory.Identifier(
            SyntaxFactory.TriviaList(),
            SyntaxKind.NameOfKeyword,
            "nameof",
            "nameof",
            SyntaxFactory.TriviaList()))
    

    不幸的是,没有作为 SyntaxFactory。表达式类型

    问题: 我正在编写一个代码生成器(使用Roslyn),它使用 的名称 .当我尝试编译时 SyntaxTree 与Roslyn一起,它因错误而失败” 错误CS0103:名称“nameof”在当前上下文中不存在 ”,而当我解析为 SyntaxTree 相同的全文 SyntaxTree 编译完成时没有任何错误。

    这是我在StackOverflow上的第一篇帖子,提前感谢您的理解。

    我正在编写一个代码生成器(使用Roslyn),它使用 的名称 。我的编制单位编制情况如下:

    private static CompilationUnitSyntax PrepareCompilationUnit(bool useNameof)
    {
        ArrowExpressionClauseSyntax arrowExpression = useNameof
            ? SyntaxFactory.ArrowExpressionClause(
                SyntaxFactory.InvocationExpression(
                    SyntaxFactory.IdentifierName(
                            SyntaxFactory.Token(SyntaxKind.NameOfKeyword).ToString()),
                        SyntaxFactory.ArgumentList(
                            SyntaxFactory.SeparatedList(
                                new[] { SyntaxFactory
                                            .Argument(SyntaxFactory
                                                .IdentifierName(PROPERTY_NAME)) }))))
            : SyntaxFactory.ArrowExpressionClause(
                    SyntaxFactory.LiteralExpression(
                        SyntaxKind.StringLiteralExpression,
                        SyntaxFactory.Literal(PROPERTY_NAME)));
    
        PropertyDeclarationSyntax property = SyntaxFactory.PropertyDeclaration(
                SyntaxFactory.ParseTypeName("string"),
                SyntaxFactory.ParseName(PROPERTY_NAME).ToString())
                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                    .WithExpressionBody(arrowExpression)
                    .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
    
        ClassDeclarationSyntax classDefinition =
            SyntaxFactory
                .ClassDeclaration(
                    SyntaxFactory
                        .Identifier(
                            SyntaxFactory
                                .ParseTypeName(CLASS_NAME).ToString()))
                .AddModifiers(
                    SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                .AddMembers(new[] { property });
    
        NamespaceDeclarationSyntax @namespace = SyntaxFactory
            .NamespaceDeclaration(
                SyntaxFactory
                    .ParseName(NAMESPACE_NAME))
                    .AddMembers(classDefinition)
            .NormalizeWhitespace();
    
        return SyntaxFactory
            .CompilationUnit()
            .WithMembers(
                SyntaxFactory
                    .SingletonList<MemberDeclarationSyntax>(@namespace));
    }
    

    编译单元准备好后,我尝试编译它,并用下面列出的4种方式创建已准备好的类的实例:

    1. 使用 的名称 关键字

      1. 从编译单元获取文本 SyntaxTree ,然后构建新的 SyntaxTree 基于此文本,并编译新的 SyntaxTree -> 它有效->我得到一个正确创建的实例

      2. 新建 SyntaxTree 基于编译单元 SyntaxTree ,并编译新的 SyntaxTree -> 它不起作用->我得到一个“ 错误CS0103:名称“nameof”在当前上下文中不存在 在编译期间

    2. 使用文字字符串

      1. 从编译单元SyntaxTree获取文本,然后构建新的 SyntaxTree 基于此文本,并编译新的 SyntaxTree -> 它有效->我得到一个正确创建的实例

      2. 新建 SyntaxTree 基于编译单元 SyntaxTree, 并编译新的 SyntaxTree -> 它有效->我得到一个正确创建的实例

    主要功能如下:

    private static void Main(string[] args)
    {
        CompilationUnitSyntax compilationUnit = PrepareCompilationUnit(useNameof: true);
        
        SaveToFile(compilationUnit);
        
        object test1 = CreateInstance(
            compilationUnit: compilationUnit,
            compileFromParsedString: true); // return instance
        object test2 = CreateInstance(
            compilationUnit: compilationUnit,
            compileFromParsedString: false); // return null
        
        compilationUnit = PrepareCompilationUnit(useNameof: false);
        
        SaveToFile(compilationUnit);
        
        test1 = CreateInstance(
            compilationUnit: compilationUnit,
            compileFromParsedString: true); // return instance
        test2 = CreateInstance(
            compilationUnit: compilationUnit,
            compileFromParsedString: false); // return instance
    }
    

    CreateInstance代码:

    private static object CreateInstance(
        CompilationUnitSyntax? compilationUnit,
        bool compileFromParsedString = false)
    {
        object result = null;
    
        var options = new CSharpParseOptions(LanguageVersion.Latest);
    
        var syntaxTree = compileFromParsedString
            ? SyntaxFactory.ParseSyntaxTree(compilationUnit.ToFullString(), options)
            : SyntaxFactory.SyntaxTree(compilationUnit, options);
    
        var trustedAssembliesPaths = ((string)AppContext
            .GetData("TRUSTED_PLATFORM_ASSEMBLIES"))
            .Split(Path.PathSeparator);
    
        var neededAssemblies = new[]
        {
                typeof(object).Assembly.GetName().Name
        };
        List<PortableExecutableReference> references = trustedAssembliesPaths
            .Where(p => neededAssemblies.Contains(Path.GetFileNameWithoutExtension(p)))
            .Select(p => MetadataReference.CreateFromFile(p))
            .ToList();
    
        var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            .WithOverflowChecks(true)
            .WithOptimizationLevel(OptimizationLevel.Debug);
    
        var compilation = CSharpCompilation
            .Create(Guid.NewGuid().ToString("N"), options: compilationOptions)
            .AddReferences(references)
            .AddSyntaxTrees(syntaxTree);
    
        try
        {
            using MemoryStream dllStream = new MemoryStream();
            Assembly assembly = null;
            EmitResult emitResult = compilation.Emit(dllStream);
    
            if (emitResult.Success)
            {
                assembly = Assembly.Load(dllStream.ToArray());
                result = assembly.CreateInstance($"{NAMESPACE_NAME}.{CLASS_NAME}");
            }
            else
            {
                foreach (var el in emitResult.Diagnostics)
                {
                    Console.WriteLine(el);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    
        return result;
    }
    

    我发现,问题出在 的名称 表达式创建。当我使用表达式解析器而不是手动创建invocationExpression时,问题也得到了解决(创建有效实例):

    
    var parsed = SyntaxFactory.ParseExpression($"nameof({PROPERTY_NAME})"))
    
    var created = SyntaxFactory.InvocationExpression(
                    SyntaxFactory.IdentifierName(
                            SyntaxFactory.Token(SyntaxKind.NameOfKeyword).ToString()),
                            SyntaxFactory.ArgumentList(
                                SyntaxFactory.SeparatedList(
                                    new[] { SyntaxFactory
                                                .Argument(SyntaxFactory
                                                    .IdentifierName(PROPERTY_NAME)) }))))
    

    我意识到问题是由 原始上下文类型 的节点的 标识符名称语法。 是的 SyntaxKind。关键字名称 对于解析的表达式,以及 SyntaxKind。标识符令牌 为创建的一个。

    有人能告诉我如何创作吗 的名称 表达式而不解析字符串(在我看来它太硬编码了)?

    生成的代码(使用 的名称 和字符串文字):

    namespace namespaceExample
    {
        public class ClsExample
        {
            public string TestName => nameof(TestName);
        }
    }
    
    namespace namespaceExample
    {
        public class ClsExample
        {
            public string TestName => "TestName";
        }
    }
    
    0 回复  |  直到 3 年前
        1
  •  0
  •   Youssef13    3 年前

    我正在编写一个代码生成器(使用Roslyn),它使用的名称为。当我试图用Roslyn编译准备好的SyntaxTree时,它失败了,错误为“错误CS0103:当前上下文中不存在名称'nameof'”,而当我解析为SyntaxTree时,相同Syntax树编译的全文没有错误。

    这意味着您正在创建一个编译器无法识别的格式错误的树。可以人为地创建对应于同一文本的不同树,但有一个树是保证正确的,这是编译器在解析该文本时产生的形式。

    要知道如何在给定树的字符串表示形式下构造给定树,最简单的方法是使用 Roslyn Quoter

    创建调用语法名称的正确代码是:

                                        SyntaxFactory.InvocationExpression(
                                            SyntaxFactory.IdentifierName(
                                                SyntaxFactory.Identifier(
                                                    SyntaxFactory.TriviaList(),
                                                    SyntaxKind.NameOfKeyword,
                                                    "nameof",
                                                    "nameof",
                                                    SyntaxFactory.TriviaList())))
                                        .WithArgumentList(
                                            SyntaxFactory.ArgumentList(
                                                SyntaxFactory.SingletonSeparatedList<ArgumentSyntax>(
                                                    SyntaxFactory.Argument(
                                                        SyntaxFactory.IdentifierName(PROPERTY_NAME)))))
    
    推荐文章