C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类

时间:2024-04-01 09:05:50

在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类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

测试结果:

C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类

后来联系到设计模式的工厂模式,试想在项目中包含接口文件,而项目的实现类不包含在项目中,在某个指定的文件中,这样就可以随时修改实现类的文件而不需要重新编译整个项目,于是在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

相关文章