如何通过配置在.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,并从中提取结果值。效果很好。谢谢大家的回复。

如何通过配置在.net (c#)类中动态地创建和执行方法

这里有两个动态执行的例子。我没有使用,但我不能进一步评论。

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);
    }
}