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 studio sdk 配置
android studio在启动后会一直处于 fetching Android sdk compoment information 状态 解决办法: 按照网友提供的方法: 第一步: 1)进入刚安装的 ...
- Java8新特性--lamada详解
最近玩了一下这个,感觉挺有趣的,语法使用起来很简洁,让代码看起来挺清爽易读的. 看了一下源码,发现挺充分的利用了jak1.5的特性(注解.泛型). 但是,具体的实现流程还是有点不通透,先Mark,等用 ...
- 推荐系统学习--cb+cf 初见
对于推荐系统的推出有两个条件:1.信息过载 ,2用户没有明确的需求 推荐系统算法中常见的有基于内容推荐,协同过滤推荐,协同过滤还可以分为基于人的协同过滤,基于内容协同过滤:社会推荐等 如何理解这些推荐 ...
- 计算机启动boot
原创博文:转载请标明出处:http://www.cnblogs.com/zxouxuewei 零.boot的含义 先问一个问题,"启动"用英语怎么说? 回答是boot.可是,boo ...
- css伪类选择器及伪元素选择器
1.类选择器 在css中可以使用类选择器把相同的元素定义成不同的样式.比如: 结果如下: 标题背景未变 2.伪类选择器 类选择器和伪类选择器的区别在于,类选择器我们定义的,而伪类选择器是CSS中已经定 ...
- android studio 中 添加.so 文件
传送门: http://jingyan.baidu.com/article/e3c78d644baaf23c4d85f57d.html 注意在Build.gradle中 添加的位置
- ayase系列
[冒个泡]技能视觉效果の自定义 by ayase [11-09 V2.4]任务数量转换属性点的lua脚本 [335]瞎倒腾的item.dbc生成工具..
- Linux-磁盘及网络IO工作方式解析
PIO与DMA 有必要简单地说说慢速I/O设备和内存之间的数据传输方式. PIO我们拿磁盘来说,很早以前,磁盘和内存之间的数据传输是需要CPU控制的,也就是说如果我们读取磁盘文件到内存中,数据要经过C ...
- 使用vue给导航栏添加链接
如下面的导航栏,使用vue技术给该导航栏增加链接: js代码为: navigation:function(){ new Vue({ el: '#navUl', data: { menuData:{ ' ...
- C++工程目录架构
项目目录结构的问题基本上是个仁者见仁,智者见智的问题,只要自己用着顺手,使用什么样的目录组织结构是没有什么大碍的.当然如果项目很大,参与的人员很多,那么合理的组织一下目录结构还是会有很大的益处的.不同 ...