动态使用运行时编译的程序集

时间:2013-06-04 13:04:09

标签: c# .net compiler-construction code-injection dynamic-language-runtime

我从代码中动态编译代码:

            string code = @"
                namespace CodeInjection
                {
                    public static class DynConcatenateString
                    {
                        public static string Concatenate(string s1, string s2){
                            return s1 + "" ! "" + s2;
                        }
                    }
                }";

            // http://stackoverflow.com/questions/604501/generating-dll-assembly-dynamically-at-run-time
            Console.WriteLine("Now doing some injection...");

            Console.WriteLine("Creating injected code in memory");


            CSharpCodeProvider codeProvider = new CSharpCodeProvider();
            ICodeCompiler icc = codeProvider.CreateCompiler();
            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateExecutable = false;
            parameters.GenerateInMemory = true;
            //parameters.OutputAssembly = "DynamicCode.dll"; // if specified creates the DLL
            CompilerResults results = icc.CompileAssemblyFromSource(parameters, code);

然后我可以用反射调用方法:

                Console.WriteLine("Input two strings, and I will concate them with reflection:");
                var s1 = Console.ReadLine();
                var s2 = Console.ReadLine();
                var result = (string)results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString").GetMethod("Concatenate").Invoke(null, new object[] { s1, s2 });

                Console.WriteLine();
                Console.WriteLine("Result:");
                Console.WriteLine(result);

但我想调用这样的东西:

                Console.WriteLine("Input two strings, and I will concate them with dynamic type:");
                var s1 = Console.ReadLine();
                var s2 = Console.ReadLine();

                dynamic type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
                var resultA = (string)type.Concatenate(s1, s2); // runtime error
                // OR
                var resultB = (string)CodeInjection.DynConcatenateString.Concatenate(s1, s2); // compile error (cannot find assembly)


                Console.WriteLine();
                Console.WriteLine("Result:");
                Console.WriteLine(resultA);
                Console.WriteLine(resultB);

结果B会更好。有什么想法怎么做?我需要严格的.NET 4.0,我们还没有更新到4.5(因为团队的一半使用VS 2010)。 (我可以用反射来调用,我知道,我只是在寻找另一种方式,因为我们需要测试dyn。代码)

4 个答案:

答案 0 :(得分:4)

在此方案中,您无法直接使用dynamicdynamic总是需要一个类实例,但是你试图在静态类中调用一个方法,因此你没有一个类实例。

但是,您可以创建一个帮助程序类,并将其与dynamic结合使用:

public class StaticMethodInvoker : DynamicObject
{
    Type _containingType;

    public StaticMethodInvoker(Type containingType)
    {
        _containingType = containingType;
    }

    public override bool TryInvokeMember(
        InvokeMemberBinder binder, Object[] args, out Object result)
    {
        result = _containingType.InvokeMember
            binder.Name,
            BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public,
            null, null, args);
        return true;
    }
}

用法:

var type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
dynamic DynConcatenateString = new StaticMethodInvoker(type);
string result = DynConcatenateString.Concatenate(s1, s2);

答案 1 :(得分:2)

您正在调用static方法。使用dynamic,您可以绕过支票,但实际上您正试图在Concatenate()上为System.Type拨打CodeInjection.DynConcatenateString

首先使它成为一个实例方法:

public class DynConcatenateString
{
    public string Concatenate(string s1, string s2){
        return s1 + "" ! "" + s2;
    }
}

现在让我们看看你的代码:

dynamic type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");

这是System.Type,而不是CodeInjection.DynConcatenateString类型的对象。如果您将dynamic更改为var,则会在编译时看到正确的类型。然后,您必须创建该类型的实例,如下所示:

var type = results.CompiledAssembly.GetType("CodeInjection.DynConcatenateString");
dynamic instance = Activator.CreateInstance(type);

对于语法B,C#没有希望,因为CodeInjection.DynConcatenateString在编译时不存在,那么该行将失败。

如果您必须保持静态,那么您所能做的就是使用反射来调用该方法(dynamic然后无用)。不要担心性能损失...... DLR并不比普通的反射快得多(作为AFAIK,它是如何通过缓存来实现的。)

答案 2 :(得分:0)

为什么不使用泛型类型? 定义您的类接口:


    public interface IDynConcatenateString 
    {
        string Concatenate(string s1, string s2);
    }

然后生成动态代码

    public T GetInstanceOf<T>(string code, string typename)
    {
        Console.WriteLine("Now doing some injection...");

        Console.WriteLine("Creating injected code in memory");


        CSharpCodeProvider codeProvider = new CSharpCodeProvider();
        ICodeCompiler icc = codeProvider.CreateCompiler();
        CompilerParameters parameters = new CompilerParameters();
        parameters.GenerateExecutable = false;
        parameters.GenerateInMemory = true;
        //parameters.OutputAssembly = "DynamicCode.dll"; // if specified creates the DLL
        CompilerResults results = icc.CompileAssemblyFromSource(parameters, code);

        //type name = "CodeInjection.DynConcatenateString"

        T codeclass = (T)results.CompiledAssembly.CreateInstance(typename);

        return codeclass;

    }

尝试像这样执行......

public void Exec() 
    {
        string code = @"
            namespace CodeInjection
            {
                public class MyDynConcatenateString : IDynConcatenateString 
                {
                    public string Concatenate(string s1, string s2){
                        return s1 + "" ! "" + s2;
                    }
                }
            }";
        IDynConcatenateString writer = GetInstanceOf<IDynConcatenateString>(
            code, "CodeInjection.MyDynConcatenateString");

        var s1 = Console.ReadLine();
        var s2 = Console.ReadLine();
        var result = writer.Concatenate(s1, s2);

        Console.WriteLine(result);

    }

IDynConcatenateString实现是动态的,在“code”变量中定义,作为Exec方法的参数。 您只需要定义接口,而无需对方法名称或类名进行硬编码。

答案 3 :(得分:0)

我认为这是一个有趣的用法,所以我在我的开源可移植库LateType

中为Dynamitey的构造函数LateType(Assembly assembly, String typename)制作了便利版本
dynamic DynConcatenateString = new DynamicObjects.LateType(cr.CompiledAssembly, "CodeInjection.DynConcatenateString");

Assert.That("1 ! 2", Is.EqualTo(DynConcatenateString.Concatenate("1","2")));