js中有一个方法eval,能够随时执行用户编写的代码。
例如:js中的代码:
eval("alert('hello world');");将会弹出一个写有hello world的提示框。
但C#中却没有对应的方法。
Google了一下,网站http://www.ckode.dk/programming/eval-in-c-yes-its-possible/基本实现了两个参数的eval。但是,对于不定参数并不适用。其实编写C#版的eval的难点在于c#是强类型语言。琢磨了许久,终于碰巧写出了。以下是相关类:
using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using Microsoft.CSharp; public class Arg { public string _type; public string _name; public Arg(string type, string name) { this._type = type; this._name = name; } } public class EvalProvider { private string errorMessage = ""; public string getErrorMessage() { return this.errorMessage; } public MethodInfo CreateMethod(List<Arg> args, Type returnType, string code, string[] usingStatements = null, string[] assemblies = null) { var includeUsings = new HashSet<string>(new[] { "System" }); includeUsings.Add(returnType.Namespace); string argStr = ""; foreach (var arg in args) { try { Type t = GetTypeByString(arg._type); includeUsings.Add(t.Namespace); argStr += ", " + arg._type + " " + arg._name; } catch { errorMessage = "uncompleted arg type: " + arg._type; return null; } } if (!argStr.Equals("")) { argStr = argStr.Substring(2); } if (usingStatements != null) foreach (var usingStatement in usingStatements) includeUsings.Add(usingStatement); MethodInfo method; using (CSharpCodeProvider compiler = new CSharpCodeProvider()) { var name = "F" + Guid.NewGuid().ToString().Replace("-", string.Empty); var includeAssemblies = new HashSet<string>(new[] { "system.dll" }); if (assemblies != null) foreach (var assembly in assemblies) includeAssemblies.Add(assembly); var parameters = new CompilerParameters(includeAssemblies.ToArray()) { GenerateInMemory = true }; string source = string.Format(@" {0} namespace {1} {{ public static class EvalClass {{ public static {2} Eval({3}) {{ {4} }} }} }}", GetUsing(includeUsings), name, returnType.Name, argStr, code); var compilerResult = compiler.CompileAssemblyFromSource(parameters, source); var compiledAssembly = compilerResult.CompiledAssembly; var type = compiledAssembly.GetType(string.Format("{0}.EvalClass", name)); method = type.GetMethod("Eval"); } return method; } private string GetUsing(HashSet<string> usingStatements) { StringBuilder result = new StringBuilder(); foreach (string usingStatement in usingStatements) { result.AppendLine(string.Format("using {0};", usingStatement)); } return result.ToString(); } /// <summary> /// 根据字符串获得类型,目前只适合简单类型。若是复杂类型,可能会抛出异常 /// </summary> /// <param name="type"></param> /// <returns></returns> public Type GetTypeByString(string type) { switch (type.ToLower()) { case "bool": return Type.GetType("System.Boolean", true, true); case "byte": return Type.GetType("System.Byte", true, true); case "sbyte": return Type.GetType("System.SByte", true, true); case "char": return Type.GetType("System.Char", true, true); case "decimal": return Type.GetType("System.Decimal", true, true); case "double": return Type.GetType("System.Double", true, true); case "float": return Type.GetType("System.Single", true, true); case "int": return Type.GetType("System.Int32", true, true); case "uint": return Type.GetType("System.UInt32", true, true); case "long": return Type.GetType("System.Int64", true, true); case "ulong": return Type.GetType("System.UInt64", true, true); case "object": return Type.GetType("System.Object", true, true); case "short": return Type.GetType("System.Int16", true, true); case "ushort": return Type.GetType("System.UInt16", true, true); case "string": return Type.GetType("System.String", true, true); case "date": case "datetime": return Type.GetType("System.DateTime", true, true); case "guid": return Type.GetType("System.Guid", true, true); default: return Type.GetType(type, true, true); } } }由于其中的方法GetTypeByString不能获得所有的类型,因此该C#版的eval不能支持所有的类型,只支持一些基本类型。
调用方法:
EvalProvider eval=new EvalProvider(); List<Arg> argList = new List<Arg>(); argList.Add(new Arg("int", "a")); argList.Add(new Arg("int", "b")); argList.Add(new Arg("string", "c")); var method= eval.CreateArgMethod(argList, eval.GetTypeByString("string"), @"return ""Hello world "" + (a + b) + c;"); object result = method.Invoke(null, new object[] { 2, 2 , " never mind!"}); Console.WriteLine((string)Convert.ChangeType(result, eval.GetTypeByString("string")));
Hello world 4 never mind!