能否对C#代码块进行序列化?

23
我将使用带有 .NET 3.5 的 C#。是否可以序列化一段代码块,传输它,在另一个地方反序列化它,然后执行它?
这样的一个例子用法是:
Action<object> pauxPublish = delegate(object o)
{
    if (!(o is string))
    {
        return;
    }
    Console.WriteLine(o.ToString());
};
Transmitter.Send(pauxPublish);

使用一些远程程序执行:

var action = Transmitter.Recieve();
action("hello world");

我的最终目标是能够在不同的进程中执行任意代码(该进程没有先前了解代码的知识)。

在为一位客户工作时,他们每天需要处理来自几百个来源的数千个文件导入。我创建了一个通用的导入工具,高级用户可以定义文件列并输入一行C#代码将输入值转换为适当的类型/格式。一旦保存了新的定义,我们会动态编译此源的导入类,然后在新文件到达时执行它。有大约50个常见情况是可自动完成的,它的效果出奇地好,并且节省了很多变更请求,同时将导入速度提高了一个数量级。 - Basic
7个回答

20

是的!!!

我们为了性能考虑,对于一个非常实际的案例,做出了这样的操作。由于性能原因,无法在运行时或使用DSL选项。我们将代码编译成程序集,然后从方法中提取IL。然后,我们通过XML序列化整个与该方法关联的元数据,进行压缩,并将其放入我们的数据库中。

在重新激活时,我们使用 DynamicMethod 类使用元数据重新生成IL,并执行它。

我们之所以这样做是因为速度问题。我们有成千上万的小代码块。不幸的是,编译代码块并即时运行至少需要250毫秒,这对我们来说太慢了。我们采取了这种方法,而且效果非常好。在运行时,重构方法和运行它的时间无法被测量。

唯一需要注意的是,签名程序集和未签名程序集不能混合使用序列化的方法数据。


这是我想出并一直在使用的方法。最大的问题似乎是将MethodBody给出的字节数组转换为OpCodes以便与Reflection.Emit一起使用。我正在查看http://weblogs.asp.net/rosherove/archive/2006/04/25/methodsvisualizer.aspx,这可能会有所帮助。 - ICR
o0o 这听起来像是我正在寻找的方法。如果您有更多与此相关的资源,链接将非常感激 :) - NoizWaves
3
我无法确定你所做的是惊人还是可怕。可能两者兼有。 - Anthony
http://blogs.msdn.com/haibo_luo/archive/2006/11/07/turn-methodinfo-to-dynamicmethod.aspx 似乎也非常有用,甚至更加实用。 - ICR
惊人还是糟糕?我认为两者都有一点。我真的除了认为这是一个巨大的黑客行为之外,什么也做不了......当它最终工作时,我感到很惊讶......震惊于解决方案实际上是多么邪恶/复杂。 - Brian Genisio
我们的方法是基于这篇文章:http://www.codeproject.com/KB/cs/ExpressionEval.aspx。我要补充一点,自从我们使用这个方法(并进行了大量单元测试)以来,我们没有遇到任何问题。 - Brian Genisio

7
你可以尝试在项目中使用IronPython。在Python中实现你所要求的功能非常简单。Python代码可以调用你的C#方法。至于安全性,你可以在某种受限制的环境中执行代码(一个例子是RestrictedPython)。

4

一般来说,这听起来像是一个非常糟糕的想法和一个巨大的安全漏洞。

你不希望另一个进程执行任何代码。理解你真正需要另一个进程做什么,并围绕它构建一个小型DSL。


绝对地,你不能信任调用者,你真的应该想出一种领域专用语言(DSL)。看看MGrammar和Oslo。 - justin.m.chase
执行任意代码并/或将其存储在数据库中...可能会出什么问题呢?对我来说似乎不像是反模式;-) - Jacobs Data Solutions

3
你可以将代码作为字符串发送,然后使用CodeDomProvider进行编译,获得相同的结果。以下是示例代码:

你可以将代码作为字符串发送,然后使用CodeDomProvider进行编译,获得相同的结果。以下是示例代码:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;

namespace DynamicCodeApplication
{
    class azCodeCompiler
    {
        private List<string> assemblies;

        public azCodeCompiler()
        {
            assemblies = new List<string>();
            scanAndCacheAssemblies();
        }

        public Assembly BuildAssembly(string code)
        {

            CodeDomProvider prov = CodeDomProvider.CreateProvider("CSharp");
            string[] references = new string[] { };   // Intentionally empty, using csc.rsp
            CompilerParameters cp = new CompilerParameters(references)
                                        {
                                            GenerateExecutable = false,
                                            GenerateInMemory = true
                                        };
            string path = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
            cp.CompilerOptions = "@" + path + @"\csc.rsp";
            CompilerResults cr = prov.CompileAssemblyFromSource(cp, code);

            foreach (CompilerError err in cr.Errors)
            {
                Console.WriteLine(err.ToString());
            }
            return cr.CompiledAssembly;
        }

        public object ExecuteCode(string code,
                                  string namespacename, string classname,
                                  string functionname, bool isstatic, params object[] args)
        {
            object returnval = null;
            Assembly asm = BuildAssembly(code);
            object instance = null;
            Type type = null;
            if (isstatic)
            {
                type = asm.GetType(namespacename + "." + classname);
            }
            else
            {
                instance = asm.CreateInstance(namespacename + "." + classname);
                type = instance.GetType();
            }
            MethodInfo method = type.GetMethod(functionname);
            returnval = method.Invoke(instance, args);
            return returnval;
        }

        private void scanAndCacheAssemblies()
        {

            /*
            foreach (string str in Directory.GetFiles(@"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727"))
            {
                if (str.Contains(".dll"))
                {
                    foreach (string st in str.Split(new char[] { '\\' }))
                    {
                        if (st.Contains(".dll"))
                        {
                            assemblies.Add(st);
                        }
                    }
                }
            }
             * */

            assemblies.Add("Accessibility.dll");
            assemblies.Add("AspNetMMCExt.dll");
            assemblies.Add("cscompmgd.dll");
            assemblies.Add("CustomMarshalers.dll");
            assemblies.Add("IEExecRemote.dll");
            assemblies.Add("IEHost.dll");
            assemblies.Add("IIEHost.dll");
            assemblies.Add("Microsoft.Build.Conversion.dll");
            assemblies.Add("Microsoft.Build.Engine.dll");
            assemblies.Add("Microsoft.Build.Framework.dll");
            assemblies.Add("Microsoft.Build.Tasks.dll");
            assemblies.Add("Microsoft.Build.Utilities.dll");
            assemblies.Add("Microsoft.Build.VisualJSharp.dll");
            assemblies.Add("Microsoft.CompactFramework.Build.Tasks.dll");
            assemblies.Add("Microsoft.JScript.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.Data.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.dll");
            assemblies.Add("Microsoft.VisualBasic.dll");
            assemblies.Add("Microsoft.VisualBasic.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.Vb.CodeDOMProcessor.dll");
            assemblies.Add("Microsoft_VsaVb.dll");
            assemblies.Add("mscorlib.dll");
            assemblies.Add("sysglobl.dll");
            assemblies.Add("System.configuration.dll");
            assemblies.Add("System.Configuration.Install.dll");
            assemblies.Add("System.Data.dll");
            assemblies.Add("System.Data.OracleClient.dll");
            assemblies.Add("System.Data.SqlXml.dll");
            assemblies.Add("System.Deployment.dll");
            assemblies.Add("System.Design.dll");
            assemblies.Add("System.DirectoryServices.dll");
            assemblies.Add("System.DirectoryServices.Protocols.dll");
            assemblies.Add("System.dll");
            assemblies.Add("System.Drawing.Design.dll");
            assemblies.Add("System.Drawing.dll");
            assemblies.Add("System.EnterpriseServices.dll");
            assemblies.Add("System.Management.dll");
            assemblies.Add("System.Messaging.dll");
            assemblies.Add("System.Runtime.Remoting.dll");
            assemblies.Add("System.Runtime.Serialization.Formatters.Soap.dll");
            assemblies.Add("System.Security.dll");
            assemblies.Add("System.ServiceProcess.dll");
            assemblies.Add("System.Transactions.dll");
            assemblies.Add("System.Web.dll");
            assemblies.Add("System.Web.Mobile.dll");
            assemblies.Add("System.Web.RegularExpressions.dll");
            assemblies.Add("System.Web.Services.dll");
            assemblies.Add("System.Windows.Forms.dll");
            assemblies.Add("System.XML.dll");
            assemblies.Add("vjscor.dll");
            assemblies.Add("vjsjbc.dll");
            assemblies.Add("vjslib.dll");
            assemblies.Add("vjslibcw.dll");
            assemblies.Add("vjssupuilib.dll");
            assemblies.Add("vjsvwaux.dll");
            assemblies.Add("vjswfc.dll");
            assemblies.Add("VJSWfcBrowserStubLib.dll");
            assemblies.Add("vjswfccw.dll");
            assemblies.Add("vjswfchtml.dll");
            assemblies.Add("Accessibility.dll");
            assemblies.Add("AspNetMMCExt.dll");
            assemblies.Add("cscompmgd.dll");
            assemblies.Add("CustomMarshalers.dll");
            assemblies.Add("IEExecRemote.dll");
            assemblies.Add("IEHost.dll");
            assemblies.Add("IIEHost.dll");
            assemblies.Add("Microsoft.Build.Conversion.dll");
            assemblies.Add("Microsoft.Build.Engine.dll");
            assemblies.Add("Microsoft.Build.Framework.dll");
            assemblies.Add("Microsoft.Build.Tasks.dll");
            assemblies.Add("Microsoft.Build.Utilities.dll");
            assemblies.Add("Microsoft.Build.VisualJSharp.dll");
            assemblies.Add("Microsoft.CompactFramework.Build.Tasks.dll");
            assemblies.Add("Microsoft.JScript.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.Data.dll");
            assemblies.Add("Microsoft.VisualBasic.Compatibility.dll");
            assemblies.Add("Microsoft.VisualBasic.dll");
            assemblies.Add("Microsoft.VisualBasic.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.dll");
            assemblies.Add("Microsoft.Vsa.Vb.CodeDOMProcessor.dll");
            assemblies.Add("Microsoft_VsaVb.dll");
            assemblies.Add("mscorlib.dll");
            assemblies.Add("sysglobl.dll");
            assemblies.Add("System.configuration.dll");
            assemblies.Add("System.Configuration.Install.dll");
            assemblies.Add("System.Data.dll");
            assemblies.Add("System.Data.OracleClient.dll");
            assemblies.Add("System.Data.SqlXml.dll");
            assemblies.Add("System.Deployment.dll");
            assemblies.Add("System.Design.dll");
            assemblies.Add("System.DirectoryServices.dll");
            assemblies.Add("System.DirectoryServices.Protocols.dll");
            assemblies.Add("System.dll");
            assemblies.Add("System.Drawing.Design.dll");
            assemblies.Add("System.Drawing.dll");
            assemblies.Add("System.EnterpriseServices.dll");
            assemblies.Add("System.Management.dll");
            assemblies.Add("System.Messaging.dll");
            assemblies.Add("System.Runtime.Remoting.dll");
            assemblies.Add("System.Runtime.Serialization.Formatters.Soap.dll");
            assemblies.Add("System.Security.dll");
            assemblies.Add("System.ServiceProcess.dll");
            assemblies.Add("System.Transactions.dll");
            assemblies.Add("System.Web.dll");
            assemblies.Add("System.Web.Mobile.dll");
            assemblies.Add("System.Web.RegularExpressions.dll");
            assemblies.Add("System.Web.Services.dll");
            assemblies.Add("System.Windows.Forms.dll");
            assemblies.Add("System.XML.dll");
            assemblies.Add("vjscor.dll");
            assemblies.Add("vjsjbc.dll");
            assemblies.Add("vjslib.dll");
            assemblies.Add("vjslibcw.dll");
            assemblies.Add("vjssupuilib.dll");
            assemblies.Add("vjsvwaux.dll");
            assemblies.Add("vjswfc.dll");
            assemblies.Add("VJSWfcBrowserStubLib.dll");
            assemblies.Add("vjswfccw.dll");
            assemblies.Add("vjswfchtml.dll");


            return;
        }
    }
}

那将是非常有帮助的,谢谢Tarks!继续这个话题,是否可能将C#代码块转换为字符串? - NoizWaves

1
将其编译为单独的程序集,发送该程序集,并让其他进程加载它。
您可能需要考虑安全性问题。 更新:另一个想法是生成表达式树并使用此库对其进行序列化:

http://www.codeplex.com/metalinq/


1

这是一个有趣的挑战,但你应该描述一下为什么想要这样做,因为根据你的目标,有很多不同的方法。正如humpohl所指出的那样,还存在一些相当严重的安全问题。

“序列化代码”可以是源代码或已编译的程序集,具体取决于您的要求。您可能不需要使用单独的代码序列化格式。

如果您想动态生成代码并将其传递,可以使用CodeDOM生成代码并编译它。但是,您很可能不需要生成完全任意的代码。


0
另一个选择是使用DLR,并限制代码执行...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接