C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类
在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类Expression,利用.net的动态编译技术实现,代码如下:
public class Expression
{
/// <summary>
/// 执行一个表达式,或代码片段
/// </summary>
/// <param name="expression">表达式或代码片段</param>
/// <param name="parameters">表达式中的变量与实例的映射关系</param>
/// <returns>执行结果</returns>
public static object Eval(string expression, SortedDictionary<string, object> parameters = null)
{
try
{
parameters = parameters ?? new SortedDictionary<string, object>();
var objects = parameters.Values.ToList();
var types = objects.Select(x => x.GetType()).ToList();
var func = GetFunc(expression, parameters.Keys.ToList(), types); var funcTypes = types.ToList();
funcTypes.Add(typeof(object));
var type = Type.GetType("System.Func`" + funcTypes.Count); if (type == null) return null; type = type.MakeGenericType(funcTypes.ToArray()); var rst = type.InvokeMember("Invoke", BindingFlags.InvokeMethod, null, func, objects.ToArray());
return rst;
}
catch (Exception)
{
return null;
}
} private static object GetFunc(string expression, List<string> alias, List<Type> types)
{
string clsName = string.Format("Class{0}", Guid.NewGuid()).Replace("-", "_");
const string methodName = "ReturnFunc";
var strCode = CreateCode(clsName, methodName, expression, alias, types); CompilerResults result = Compile(strCode); Assembly assembly = result.CompiledAssembly;
Type aType = assembly.GetType(string.Format("NSName.{0}", clsName));
MethodInfo method = aType.GetMethod(methodName);
return method.Invoke(null, null);
} private static string CreateCode(string clsName, string methodName, string expression, List<string> alias, List<Type> types)
{
var ts = string.Empty;
if (types != null && types.Any())
{
ts = string.Join(",", types.Select(x => x.FullName)) + ",";
}
ts += "System.Object"; var strAlias = string.Format("({0})", string.Join(",", alias));
return @"
using System;
using System.Linq;
using System.Collections.Generic;
namespace NSName
{
internal static class " + clsName + @"
{
public static Func<" + ts + @"> " + methodName + @"()
{
return " + strAlias + @" => " + expression + @";
}
}
}";
} private static CompilerResults Compile(string code, List<string> assemblyPaths = null)
{
var provider = new CSharpCodeProvider();
var parameter = new CompilerParameters(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
assemblyPaths = assemblyPaths ?? new List<string>();
assemblyPaths.AddRange(assemblies.Where(x => !x.IsDynamic).Select(x => x.Location)); foreach (var assemblyPath in assemblyPaths)
{
if (!string.IsNullOrWhiteSpace(assemblyPath) && !parameter.ReferencedAssemblies.Contains(assemblyPath))
parameter.ReferencedAssemblies.Add(assemblyPath);
} parameter.GenerateExecutable = false;
parameter.GenerateInMemory = true; //将你的式子放在这里
CompilerResults result = provider.CompileAssemblyFromSource(parameter, code);
if (result.Errors.Count > )
{
var errorMsg = result.Errors.OfType<CompilerError>()
.Aggregate(string.Empty, (current, error) => current + error.ToString());
throw new Exception(errorMsg);
} return result;
}
}
测试准备代码:
public class Model1
{
public int X = ;
public int Y = ; public int Cal()
{
return X / Y;
}
} public class Model2
{
public int Z = ;
public bool B = false; public List<int> List = new List<int>
{
,,,,,,
};
} public class Model3
{
public Model3()
{
var s = string.Join(",",List);
}
public List<TObj> List = new List<TObj>
{
new TObj{Age = ,Name = "Hol"},
new TObj{Age = ,Name = "Luk"},
new TObj{Age = ,Name = "Jim"},
new TObj{Age = ,Name = "Tom"},
};
}
Models
测试代码:
static void Main()
{
object result; var x = new Model1();
var y = new Model2();
var z = new Model3(); result = Expression.Eval("20 - 15");
Console.WriteLine(result); result = Expression.Eval("20 > 15");
Console.WriteLine(result); result = Expression.Eval("x.X / x.Y", new SortedDictionary<string, object> { { "x", x } });
Console.WriteLine(result); result = Expression.Eval("x.X + x.Cal()", new SortedDictionary<string, object> { { "x", x } });
Console.WriteLine(result); result = Expression.Eval("x.X + y.Z", new SortedDictionary<string, object> { { "x", x }, { "y", y } });
Console.WriteLine(result); result = Expression.Eval("x.X > x.Y && 20 > 15", new SortedDictionary<string, object> { { "x", x } });
Console.WriteLine(result); result = Expression.Eval("x.X > x.Y || y.B", new SortedDictionary<string, object> { { "x", x }, { "y", y } });
Console.WriteLine(result); result = Expression.Eval("x.X != y.Z", new SortedDictionary<string, object> { { "x", x }, { "y", y } });
Console.WriteLine(result); result = Expression.Eval("y.B ? (x.X - x.Y) : (x.X + x.Y)", new SortedDictionary<string, object> { { "x", x }, { "y", y } });
Console.WriteLine(result); result = Expression.Eval("y.List[4]", new SortedDictionary<string, object> { { "y", y } });
Console.WriteLine(result); result = Expression.Eval("y.List.Count", new SortedDictionary<string, object> { { "y", y } });
Console.WriteLine(result); result = Expression.Eval("{ y.List.Add(120); return y.List.Last(); }", new SortedDictionary<string, object> { { "y", y } });
Console.WriteLine(result); result = Expression.Eval("string.Join(\"\t\", y.List.OrderBy(i => i).Select(i => i.ToString()))", new SortedDictionary<string, object> { { "y", y } });
Console.WriteLine(result); result = Expression.Eval("z.List.Max(x=>x.Age)", new SortedDictionary<string, object> { { "z", z } });
Console.WriteLine(result); result = Expression.Eval("string.Join(\", \",z.List.Select(x=>x.Name));", new SortedDictionary<string, object> { { "z", z } });
Console.WriteLine(result);
}
Program
测试结果:
后来联系到设计模式的工厂模式,试想在项目中包含接口文件,而项目的实现类不包含在项目中,在某个指定的文件中,这样就可以随时修改实现类的文件而不需要重新编译整个项目,于是在Expression类中增加方法:
/// <summary>
/// 从文件中加载指定接口的实现类的实例
/// </summary>
/// <typeparam name="T">指定的接口的类型</typeparam>
/// <param name="fileName">类型加载的源文件</param>
/// <param name="assemblyPaths">加载类型依赖的程序集路径</param>
/// <returns>实例</returns>
public static T GetInterfaceInstence<T>(string fileName, List<string> assemblyPaths = null)
{
var baseType = typeof(T);
if (!baseType.IsInterface || string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName))
{
return default(T);
}
var strCode = string.Empty;
using (var file = new StreamReader(fileName, Encoding.Unicode))
{
strCode = file.ReadToEnd();
}
CompilerResults result = Compile(strCode, assemblyPaths); Assembly assembly = result.CompiledAssembly;
var ts = assembly.GetTypes().ToList();
Type aType = ts.FirstOrDefault(x => x.GetInterface(baseType.FullName) != null);
if (aType == null || !aType.IsClass)
{
return default(T);
} return (T)Activator.CreateInstance(aType);
}
关于表达式的编译,后来在网上找到一个解决方案,ExpressionEvaluator.dll
C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类的更多相关文章
- 页面上动态编译及执行java代码
本文地址:http://www.cnblogs.com/liaoyu/p/real-time-compile-and-run-java-code-web-app.html 最近看到同事在页面上编译和执 ...
- iOS Interface Builder:在.xib文件中加载另一个.xib文件
在开发中,经常会用到一个需要重复使用的模块,比如好友列表中每个用户的展示或每条动态,这些都是相同的模版,这样我们就可以把这个部分提取出来放到一个单独的.xib中.那么提取出的.xib如何在其他.xib ...
- day02编程语言,Python语言介绍,Python解释器安装,环境变量,Python代码执行,pip,应用程序使用文件的三步骤,变量,变量的三大组成,比较,pycharm
复习 重点: 1.进制转换:二进制 与十六进制 2.内存分布:栈区 与堆区 # 二进制1111转换十六进制 => 8 4 2 1 => f 10101100111011 => 2a7 ...
- 风炫安全web安全学习第三十二节课 Python代码执行以及代码防御措施
风炫安全web安全学习第三十二节课 Python代码执行以及代码防御措施 Python 语言可能发生的命令执行漏洞 内置危险函数 eval和exec函数 eval eval是一个python内置函数, ...
- python with as 以上这段代码执行完毕后,就算在处理过程中出问题了,文件 f 总是会关闭。
with open("myfile.txt") as f: for line in f: print(line, end="") 以上这段代码执行完毕后,就算在 ...
- 动态加载JS文件,并根据JS文件的加载状态来执行自己的回调函数
动态加载JS文件,并根据JS文件的加载状态来执行自己的回调函数, 在很多场景下,我们需要在动态加载JS文件的时候,根据加载的状态来进行后续的操作,需要在JS加载成功后,执行另一方法,这个方法是依托在加 ...
- Ant执行一个含有main方法的class文件
目前需要使用ant来执行一个含有main方法的class文件,并且需要通过命令来行传两个参数(start和end)到main方法. <target name="gsp" de ...
- extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
- 【原】从一个bug浅谈YUI3组件的资源加载
篇前声明:为了不涉及业务细节,篇内信息统一以某游戏,某功能代替 前不久,某游戏准备内测客户端,开发人员测试过程中发现某功能突然不灵了,之前的测试一切ok,没有发现任何异常,第一反应是,游戏内浏览器都是 ...
随机推荐
- 学android:直接用jdk来helloworld
本文稍稍了解下java的编译机制, 以及试试纯粹的jdk来helloworld 在jdk安装位置的bin 目录下看看jdk的工具 有许多的jdk的工具吧,当然,和bin同级的目录下还有jre, lib ...
- Mac 配置Spark环境scala+python版本(Spark1.6.0)
1. 从官网下载Spark安装包,解压到自己的安装目录下(默认已经安装好JDK,JDK安装可自行查找): spark官网:http://spark.apache.org/downloads.html ...
- 如何关闭emacs开启时自己打开的欢迎界面
EMACS在开启后,会自动打开一个欢迎界面.如果要去除他,可以在用emacs或其实编辑工具编辑~/.emacs文件. 在最下面一行加上,保存退出即可. (setq inhibit-startup-me ...
- JavaScript 运动框架
<script> window.onload=function (){ var oDiv=document.getElementById("div1"); oDiv.o ...
- 计算机网络自学之路------IP协议(2)
之前忘记说了,这些内容都是边看视频边写的,视频出自西安交通大学. 本期说的是IP分组转发的内容,里面详细介绍了IP协议跟ARP协议,最后再给出一个例子,说明这两个协议在网络中传输的具体过程. 1)Ip ...
- const成员变量初始化总结
const可以用来声明常量也就是说他的值不能被修改: const成员必须在定义的时候同时初始化,不能进行赋值 如 const int a:a的值不能修改,不能给它赋值,如何才能让它一开始就拥有一个值? ...
- 使用cocos2d-x3.4结合cocos2.1.5制作小游戏《亲亲小熊》
在最新的cocos集成环境中,CocosStudio已经集成到cocos中了,至于界面的制作和编辑器的基本使用在cocos官网有详细教程, 这里就不细说,资源下载和详情请参看官网教程:http://c ...
- Chromuim proxy Api 提取代里proxy调用Chrome隐身多窗口 多COOKIE 工具
Chromuim proxy Api提取proxy调用Chrome隐身 多COOKIES 多窗口工具每一个代理拥有一个独立的窗口和USERDATA 独立COOKIES 伪装UA UA:<scri ...
- Adb工具配置和设备连接
ADB全程Android Debug Bridge,是Android SDK里的一个工具,用这个工具可以直接操作管理Android模拟器或者真实的Android设备(如手机). 一.Adb工具使用配置 ...
- 新手之自动转存DLL——20150626星期五
-------------------菜鸟错例一:--------------------------------------------- int a=0; if() { int a = GetTy ...