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!