如何在DynamicMethod生成的正文中调用方法

时间:2019-06-06 10:22:52

标签: c# methods dynamic

我正在尝试在2个软件之间创建一种WCF通信(以挑战自己)。

我所说的WCF是指能够在共享库中具有作为契约的接口。然后可以在我的两个软件中使用该合同。 我想像在WCf中一样使用它们,这意味着客户端似乎在从同一软件中调用简单方法,但实际上是使用TcpClient在另一端调用TcpServer ....

作为WCF,我希望是通用的,所以我不希望预包装的类包装用于特定合同的网络逻辑。 作为WCF,我希望能够编写一个合约接口,然后使用我的合约作为模板参数创建一个诸如“ ClientBase”类的新实例,然后将该客户端用作我的其他软件的“远程”。 / p>

作为一个例子总是更好,这就是我想要的:

在共享项目中:

public interface IFooContract
{
    void Add(int a, int b);
}

在客户端中:

class Program
{
    static void Main(string[] args)
    {
        using (var client = new ClientFooContract())
        {
            var result = client.Add(5, 2);
        }
    }
}

class ClientFooContract : MyClientBase<IFooContract>, IFooContract, IDisposable
{
    public int Add(int a, int b)
    {
        return Channel.Add(a, b);
    }
}

class MyClientBase<T> where T : class
{
    protected T Channel;

    public MyClientBase()
    {
        Channel = /*Create Channel instance*/
    }
}

我的实现确实接近WCF的基础,但是我的问题是创建Channel Instance,因为我当然没有可以实例化的任何类。我需要一种动态类来实现该特定合同的实现,并针对该合同的每种方法,处理用于创建TcpClient,将其连接到远程服务器,发送数据,等待响应并将结果返回给ClientFooContract的网络逻辑。 / p>

当然,我可以创建一个实现合同并为每种方法处理网络的ChannelBase类,但是我希望能够像WCF一样通用,因为它能够为我的系统提供任何类型的合同。

我目前正在使用Emit命名空间,试图立即构建包含方法的动态类,这里是我尚未完成的用于创建方法的代码:

class Program
{
    static void Main(string[] args)
    {
        var methods = new List<Method>();

        methods.Add(new Method()
        {
            Name = "Add",
            Params = new List<MethodParam>()
            {
                new MethodParam()
                {
                    Name = "a",
                    Type = typeof(int)
                },
                new MethodParam()
                {
                    Name = "b",
                    Type = typeof(int)
                }
            },
            ReturnType = typeof(int)
        });

        MyTypeBuilder.CompileResultType(methods);
    }
}

public class Method
{
    public string Name;
    public List<MethodParam> Params;
    public Type ReturnType;
}

public class MethodParam
{
    public string Name;
    public Type Type;
}

public static class MyTypeBuilder
{
    public static Type CompileResultType(List<Method> methodList)
    {
        TypeBuilder tb = GetTypeBuilder();
        ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        if (methodList != null)
            foreach (var method in methodList)
                CreateMethod(tb, method);

        Type objectType = tb.CreateType();

        return objectType;
    }

    private static TypeBuilder GetTypeBuilder()
    {
        var typeSignature = "MyDynamicType";
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        //AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                null,
                new Type[] { typeof(IFoo) });

        return tb;
    }

    private static void CreateMethod(TypeBuilder tb, Method method)
    {
        MethodBuilder methodBuilder = tb.DefineMethod(method.Name, MethodAttributes.Public, method.ReturnType, method.Params.Select(l => l.Type).ToArray());
        ILGenerator il = methodBuilder.GetILGenerator();
    }
}

这是我的难题,我不知道如何在我的动态方法中添加主体,我在互联网上发现了一些关于如何创建局部变量的示例,等等,但是实际上我该如何用IL创建一个TcpClient,我需要吗将所有这些放置在单独的方法中,然后调用此方法?如何 ? 我实际上如何简单地调用console.writeLine来实际测试我的系统?

我的问题实际上是MethodBody的创建,因为我将不得不在这里做很多工作,而不仅仅是声明局部变量和处理基本操作。 如果至少有一种方法可以在另一个类上调用另一个方法,那么将很有帮助

如果您什么都不知道,谢谢您的帮助

1 个答案:

答案 0 :(得分:0)

经过几个小时的测试,我终于找到了解决方法主体问题的方法。

此处带有评论(以防万一,某天结束于本主题)。

class Program
{
    static void Main(string[] args)
    {
        // Init List of method that I want to create
        var methods = new List<Method>();

        methods.Add(new Method()
        {
            Name = "Add",
            Params = new List<MethodParam>()
            {
                new MethodParam()
                {
                    Name = "a",
                    Type = typeof(int)
                },
                new MethodParam()
                {
                    Name = "b",
                    Type = typeof(int)
                }
            },
            ReturnType = typeof(int)
        });

        // Compile my type
        var myType = MyTypeBuilder.CompileResultType(methods);

        // Create instance of my type
        var myObject = Activator.CreateInstance(myType) as IFooContract;

        // Call Function
        var result = myObject.Add(5, 2);

        // Log
        Console.WriteLine(result);

        Console.Read();
    }
}

// Used to store Method Infos
public class Method
{
    public string Name;
    public List<MethodParam> Params;
    public Type ReturnType;
}

// Used to store Method param's Infos
public class MethodParam
{
    public string Name;
    public Type Type;
}

// Builder for the Dynamic class
public static class MyTypeBuilder
{
    public static Type CompileResultType(List<Method> methodList)
    {
        TypeBuilder tb = GetTypeBuilder();
        ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        if (methodList != null)
        {
            // Loop throught all method definition
            foreach (var method in methodList)
            {
                // Generate new method on the dynamic class
                CreateMethod(tb, method);
            }
        }

        // Create the Dynamic class
        Type objectType = tb.CreateType();

        return objectType;
    }

    private static TypeBuilder GetTypeBuilder()
    {
        var typeSignature = "MyDynamicType";
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);

        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                null,
                new Type[] { typeof(IFooContract) }); // <= Interface that the Dynamic class will implement (used for intellisens)

        tb.AddInterfaceImplementation(typeof(IFooContract)); // <= Specify that the class will implement that interface

        return tb;
    }

    private static void CreateMethod(TypeBuilder tb, Method method)
    {
        // Create Method builder
        MethodBuilder mb = tb.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, method.Params.Select(x => x.Type).ToArray());

        // Get the IL Generator
        ILGenerator il = mb.GetILGenerator();


        // Start Build Method Body :

        // Load first parameter on evaluation stack
        il.Emit(OpCodes.Ldarg_1);

        // Load Second parameter on evaluation stack
        il.Emit(OpCodes.Ldarg_2);

        // Use the two last element loaded as operand for "+" operation
        il.Emit(OpCodes.Add);

        // Push the last element on evaluation stack as return of the function
        il.Emit(OpCodes.Ret);

        // Stop Build Method Body


        // Explicitly set this new function as the implementation of the interface function
        MethodInfo interfaceMethod = typeof(IFooContract).GetMethod(method.name);
        tb.DefineMethodOverride(mb, interfaceMethod);
    }
}