如何通过配置在.net (c#)类中动态地创建和执行方法
本文关键字:动态 创建 方法 执行 配置 何通过 net | 更新日期: 2023-09-27 18:06:21
我正在考虑MsBuild中的"内联任务"。参考:http://msdn.microsoft.com/en-us/library/dd722601.aspx
我想找到或创建一个框架,允许我通过配置重写方法。例如,如果我有一个众所周知的基类,它有一个方法Execute(args),我如何在部署时提供一个覆盖的方法实现,而不需要新的代码,构建,发布周期?我想实际插入方法体到一个配置文件或最好是一个数据库表。
我认为这可以通过代码dom,动态语言集成,或者像powershell(?)这样的东西来完成。我正在寻找推荐,或者是一个已经有人写过的库。
应用程序是用c#编写的。最好是c#的扩展,但我也愿意接受其他的想法。
Update:从技术上讲,我甚至不需要重写一个方法。只要能够动态执行一些外部源代码,传入一个参数并返回一个结果就足够了。
更新。我最终编写了代码来实例化一个PowerShell对象,并动态地执行一个脚本来返回一个值。下面是我使用的一段代码:
public static Collection<PSObject> ExecuteScript(string code, string variableName, object variableValue)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(code);
if (!string.IsNullOrWhiteSpace(variableName))
{
ps.Runspace.SessionStateProxy.SetVariable(variableName, variableValue);
}
var result = ps.Invoke();
return result;
}
然后在调用代码中,我只是检查返回值中的第一个PSObject,并从中提取结果值。效果很好。谢谢大家的回复。
这里有两个动态执行的例子。我没有使用,但我不能进一步评论。
http://www.codeproject.com/KB/dotnet/evaluator.aspx
http://www.csharpfriends.com/articles/getarticle.aspx?articleid=118
关于名称空间,在第二篇文章中,您可以通过CompilerParameter类添加程序集。
// Create the C# compiler
CSharpCodeProvider csCompiler = new CSharpCodeProvider();
ICodeCompiler iCodeCompiler = csCompiler.CreateCompiler();
// input params for the compiler
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.OutputAssembly = "CSharpFriends.dll";
compilerParams.ReferencedAssemblies.Add("system.dll");
一种选择是使用Iron Python(或其他DLR语言)。然后,Execute方法会在配置文件中查找脚本,编译它并在运行时执行。
在你的项目中包含必要的Iron Python程序集并不是一个很大的开销。
您可能需要做一些管道来将应用程序的其他部分暴露给python运行时环境,但这很容易做到。
您可以使用接口,然后在运行时解析具体的类,例如使用配置文件。在http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx
Managed Extensibility Framework (MEF)可能也很合适。它是。net 4的一部分。
http://msdn.microsoft.com/en-us/library/dd460648.aspx http://mef.codeplex.com/如果可扩展性只适用于一个方法,那么MEF将是多余的。如果您所扩展的内容将随着时间的推移而增长,那么我认为MEF将提供最健壮和长期可管理的框架。
看起来你可能想看看工厂模式;返回代表。不幸的是,您将需要一个类型来"容纳"该方法,因此您通常会生成如下代码:
namespace Dynamic {
public static int Foo(int bar) {
// .. Configured body here.
}
}
重要的是,您的工厂不生成它以前见过的方法。下面是一个例子:
static class Delegates
{
private static Func<Func<int, string>> _test;
public static Func<int, string> Test
{
get
{
return _test();
}
}
static Delegates()
{
// Use your config variables instead of the "return arg.ToString();"
CreateFactory<Func<int, string>>(x => _test = x, "return arg.ToString();");
}
private static void CreateFactory<TDelegate>(Action<Func<TDelegate>> locationSetter, string identifier)
{
locationSetter(() =>
{
var result = Generate<TDelegate>(identifier);
locationSetter(() => result);
return result;
});
}
private static string GenerateSignature<TDelegate>()
{
// Create the signature of the delegate.
var t = typeof(TDelegate);
if (!typeof(Delegate).IsAssignableFrom(t))
throw new Exception("TDelegate must be delegate type.");
var invoke = t.GetMethod("Invoke");
var sig = new StringBuilder();
// Append the return type.
if (invoke.ReturnType == typeof(void))
sig.Append("void");
else
sig.Append(invoke.ReturnType.FullName);
sig.Append(" ");
sig.Append("Invoke(");
// Append the parameters.
var param = invoke.GetParameters();
for (var i = 0; i < param.Length; i++)
{
if (i != 0)
sig.Append(", ");
sig.Append(param[i].ParameterType.FullName);
sig.Append(" ");
sig.Append(param[i].Name);
}
sig.Append(")");
return sig.ToString();
}
private static TDelegate Generate<TDelegate>(string code)
{
// Generate the containing class and method.
var codeBuilder = new StringBuilder(50);
codeBuilder.AppendLine("using System;");
codeBuilder.Append("namespace Dynamic { class DynamicClass { public static ");
codeBuilder.Append(GenerateSignature<TDelegate>());
codeBuilder.AppendLine("{");
codeBuilder.AppendLine(code);
codeBuilder.AppendLine("} } }");
var compilerVersion = new Version(1, 0, 0, 0);
// Create the compiler parameters.
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
parameters.ReferencedAssemblies.Clear();
foreach (var referenceAssembly in AppDomain.CurrentDomain.GetAssemblies())
{
parameters.ReferencedAssemblies.Add(referenceAssembly.Location);
// Figure out which version we are compiling against.
var an = new AssemblyName(referenceAssembly.FullName);
if (an.Name == "mscorlib" && compilerVersion < an.Version)
{
compilerVersion = an.Version;
}
}
var cp = new CSharpCodeProvider(
new Dictionary<string, string>() { { "CompilerVersion", string.Format("v{0}.{1}", compilerVersion.Major, compilerVersion.Minor) } }
);
var results = cp.CompileAssemblyFromSource(parameters, codeBuilder.ToString());
if (results.Errors.HasErrors)
throw new Exception("Method failed to compile.");
var assembly = results.CompiledAssembly;
if (assembly == null)
throw new Exception("Method failed to compile.");
var t = assembly.GetType("Dynamic.DynamicClass");
if (t == null)
throw new Exception("Method failed to compile.");
var m = t.GetMethod("Invoke");
if (m == null)
throw new Exception("Method failed to compile.");
return (TDelegate)(object)Delegate.CreateDelegate(typeof(TDelegate), m);
}
}