c# - 解析 C# 代码(作为字符串)并插入其他方法

标签 c# parsing reflection code-generation codedom

我有一个正在开发的 C# 应用程序,它可以远程加载它的代码,然后运行它(为了论证,您可以假设该应用程序是安全的)。

代码是 C#,但它作为 XML 文档发送,解析为字符串,然后编译和执行。

现在,我想做的 - 比我预期的要困难一些 - 能够解析整个文档,并在编译之前,在每行执行后插入额外的命令。

例如,考虑以下代码:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCode
{
    static class MyProg
    {
        static void Run()
        {
            int i = 0;
            i++;

            Log(i);
        }
    }
}

我想要的,在解析之后更像是:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCode
{
    static class MyProg
    {
        static void Run()
        {
            int i = 0;
            MyAdditionalMethod();
            i++;
            MyAdditionalMethod();

            Log(i);
            MyAdditionalMethod();
        }
    }
}

请记住明显的陷阱 - 我不能在每个分号之后都使用它,因为这在 getter/setter 中不起作用,即:

转换:

public string MyString { get; set; }

收件人:

public string MyString { get; MyAdditionalMethod(); set; MyAdditionalMethod(); }

会失败。就像类级声明、using 语句等一样。此外,在许多情况下,我还可以在花括号后添加 MyAdditionalMethod() - 比如在委托(delegate)中,紧接在 if 语句或方法声明等之后。

所以,我一直在研究 CodeDOM,这看起来可能是一个解决方案,但很难弄清楚从哪里开始。否则我会尝试解析整个事物并创建一棵我可以解析的树 - 尽管考虑到我需要考虑的案例数量,这有点困难。

有人知道其他解决方案吗?

最佳答案

我建议使用一些来自 Mono 或 SharpDevelop 的 C# 解析器,因为它们应该是最新的。我试了一下 NRefactory来自 SharpDevelop,如果你 download SharpDevelop 的源代码有一个演示和一些单元测试,可以很好地介绍它的用法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.NRefactory;
using System.IO;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.NRefactory.PrettyPrinter;

namespace Parse
{
    class Program
    {
        static void Main(string[] args)
        {
            string code = @"using System;
            using System.Collections.Generic;
            using System.Linq;

            namespace MyCode
            {
                static class MyProg
                {
                    static void Run()
                    {
                        int i = 0;
                        i++;

                        Log(i);
                    }
                }
            }
            ";

            IParser p = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(code));
            p.Parse();

            //Output Original
            CSharpOutputVisitor output = new CSharpOutputVisitor();
            output.VisitCompilationUnit(p.CompilationUnit, null);
            Console.Write(output.Text);

            //Add custom method calls
            AddMethodVisitor v = new AddMethodVisitor();
            v.VisitCompilationUnit(p.CompilationUnit, null);
            v.AddMethodCalls();
            output = new CSharpOutputVisitor();
            output.VisitCompilationUnit(p.CompilationUnit, null);

            //Output result
            Console.Write(output.Text);
            Console.ReadLine();
        }


    }

    //The vistor adds method calls after visiting by storing the nodes in a dictionary. 
    public class AddMethodVisitor : ConvertVisitorBase
    {
        private IdentifierExpression member = new IdentifierExpression("MyAdditionalMethod");

        private Dictionary<INode, INode> expressions = new Dictionary<INode, INode>();

        private void AddNode(INode original)
        {
            expressions.Add(original, new ExpressionStatement(new InvocationExpression(member)));
        }

        public override object VisitExpressionStatement(ExpressionStatement expressionStatement, object data)
        {
            AddNode(expressionStatement);
            return base.VisitExpressionStatement(expressionStatement, data);
        }

        public override object VisitLocalVariableDeclaration(LocalVariableDeclaration localVariableDeclaration, object data)
        {
            AddNode(localVariableDeclaration);
            return base.VisitLocalVariableDeclaration(localVariableDeclaration, data);
        }

        public void AddMethodCalls()
        {
            foreach (var e in expressions)
            {
                InsertAfterSibling(e.Key, e.Value);
            }
        }

    }
}

您将需要改进访问者以处理更多情况,但这是一个好的开始。

或者,您可以编译原始文件并使用 Cecil 进行一些 IL 操作。或者试试像 PostSharp 这样的 AOP 库.最后你可以看看 .NET Profiling API .

关于c# - 解析 C# 代码(作为字符串)并插入其他方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4998608/

相关文章:

c# - 当 session 过期时, session 管理器不会将我注销,HTTPContext.Current 为 Null

c# - 使用选定的 Xpath 和 for 循环删除 XML

java - 如何使用 Java 创建数据流程图

powershell - 解析文本文件并保存为 .csv

scala - 列出所有可见的隐式

java - 反射机制是否破坏了java中的安全性?请解释

c# - 输入字符串的格式不正确。在时间跨度内

c# - 除了使用线程之外,监视 C# 中的控件

javascript - 当每个单独的段都是一个对象时,如何在 d3 中创建堆积条形图?

c# - 在C#中使用反射修改循环中的字段,扩展方法