2

いくつかのコードを含む文字列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);)。乗り心地が滑らか...

4

1 に答える 1

1

私はこのようなもので行きました:

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]
于 2015-01-30T10:51:04.450 に答える