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

Ali*_*vin 11 c# reflection parsing 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

解析后我想要的更像是:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

转换:

public string MyString { get; set; }
Run Code Online (Sandbox Code Playgroud)

至:

public string MyString { get; MyAdditionalMethod(); set; MyAdditionalMethod(); }
Run Code Online (Sandbox Code Playgroud)

会失败的.和类级声明一样,使用语句等.另外,在一些情况下,我也可以在花括号后添加MyAdditionalMethod() - 比如在委托中,紧跟在if语句之后,或者方法声明等.

所以,我一直在研究CodeDOM,看起来它可能是一个解决方案,但很难弄清楚从哪里开始.我正试图解析整个事情并创建一个我可以解析的树 - 虽然这有点难,考虑到我需要考虑的案例数量.

有没有人知道其他任何解决方案?

Kri*_*is 7

有一些C#解析器,我建议使用Mono或SharpDevelop中的东西,因为它们应该是最新的.我使用SharpDevelop的NRefactory,如果你下载了SharpDevelop的源代码,那么有一个演示和一些UnitTests,它们是一个很好的介绍它的用法.

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

    }
}
Run Code Online (Sandbox Code Playgroud)

您需要改进访问者以处理更多案例,但这是一个良好的开端.

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