4

我从代码动态编译代码:

            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.code)

4

4 回答 4

4

您不能dynamic在这种情况下直接使用。dynamic总是需要一个类实例,但是您试图在静态类中调用一个方法,因此您没有类实例。

但是,您可以创建一个辅助类并将其与以下内容结合使用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);
于 2013-06-04T13:10:05.143 回答
2

你正在调用一个static方法。随着dynamic您绕过检查,但实际上您正在尝试Concatenate()调用System.Typefor 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 一样,它是通过一点缓存来实现的)。

于 2013-06-04T13:14:02.727 回答
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 方法的参数。您只需要定义接口,无需硬编码方法名或类名。

于 2013-06-04T14:27:41.110 回答
0

我认为这是一个有趣的用法,所以我在我的开源可移植库Dynamitey中制作了我的LateType构造函数的便利版本LateType(Assembly assembly, String typename)

dynamic DynConcatenateString = new DynamicObjects.LateType(cr.CompiledAssembly, "CodeInjection.DynConcatenateString");

Assert.That("1 ! 2", Is.EqualTo(DynConcatenateString.Concatenate("1","2")));
于 2013-06-07T03:33:25.017 回答