我有一个带有一些代码的字符串string input;
(下面都在那个字符串中)
var x = s.IndexOf("a");
return String.Format(s, x);
现在,我想实现以下方案:
Func<string, string> f = Compile(input);
var test = "dcba - {0}";
var result = f(test);
// result = "dcba - 3";
我假设,实际的 T1,TResult 是已知的(这里:字符串,字符串),并且输入名为"s"。我可以这样实现它:
var input = "var x = s.IndexOf("a"); return String.Format(s, x);";
var containerClass = @"using System; class TempClass {{ public string temp_func(string s){{ {0} }} }}";
var code = String.Format(containerClass, input);
// Create a new instance of the C# compiler
var compiler = new CSharpCodeProvider();
var params = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
params.ReferencedAssemblies.Add("System.dll");
var results = compiler.CompileAssemblyFromSource(params, code);
Func<string, string> f;
if (results.Errors.Count == 0)
{
f = s =>
{
var myClass = results.CompiledAssembly.CreateInstance("TempClass");
return (string) myClass.GetType().
GetMethod("temp_func").
Invoke(myClass, new object[] {s});
};
// test:
Console.WriteLine(f(Console.ReadLine()));
}
但这是相当复杂的方式。如果我知道我只想要一个Func<T1, TResult>
,而不是一个完整的编译程序集,实例化类(或在一个类上调用静态方法),有什么方法可以简化这一点?
当然,我可以采用此代码并对其进行漂亮的修饰 - 将其包装在通用类中,获取 T1、TResult 类型名称以放入TempClass
模板 ( String.Format("public {0} temp_func({1} s)",typeof(TResult).Name, typeof(T1).Name);
),但它有一种润滑方轮轴的感觉,使骑行更平稳......
我选择了这样的东西:
public class DynamicFunction
{
private static int _counter = 0;
private const string ClassBody = "{2} public static class DynamicFunctionHost{0} {{ {1} }}";
private const string ClassName = "DynamicFunctionHost{0}";
private const string FunctionName = "func";
private const string T1FuncBody = "public static {1} func({0} param1){{ {2} }}";
public static Func<T1, TResult> Get<T1, TResult>(string funcBody, string[] referenced, string[] usingNs)
{
var code = String.Format(ClassBody, _counter,
String.Format(T1FuncBody, typeof (T1).Name, typeof (TResult).Name, funcBody),
String.Join("n", usingNs.Select(r => String.Format("using {0};", r))));
var result = Compile(code, referenced);
var host =
result.CompiledAssembly.DefinedTypes.Single(
typeinfo => typeinfo.FullName.Equals(String.Format(ClassName, _counter)));
++_counter;
return input => (TResult) host.GetMethod(FunctionName).Invoke(null, new object[] { input });
}
private static CompilerResults Compile(string code, string[] referenced)
{
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
foreach (var r in referenced)
parameters.ReferencedAssemblies.Add(r);
var results = compiler.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count == 0) return results;
// else
var e = new ArgumentException("Errors during compilation", "code");
e.Data.Add("Errors", results.Errors);
throw e;
}
}
那么使用相当简单:
var f = DynamicFunction.Get<string, string[]>("return param1.ToCharArray()", new []{"System.dll","System.Core.dll"}, new []{"System"});
var x = f("abcd"); // =[a,b,c,d]