Как вызвать метод в сгенерированном теле DynamicMethod - PullRequest
0 голосов
/ 06 июня 2019

Я пытаюсь создать своего рода связь WCF между двумя программами (чтобы бросить вызов себе).

То, что я имею в виду под WCF, - это возможность иметь интерфейс в качестве контракта в общей библиотеке.Затем возможность использовать этот контракт в моих двух программах.Я хотел бы использовать их, как мы это делаем в WCf, это означает, что клиент вызывает простой метод из того же программного обеспечения, но на самом деле использует TcpClient, вызывающий TcpServer с другой стороны ....

Как WCF, я хотел бы быть универсальным, поэтому я не хочу готовый класс, который оборачивает мою сетевую логику для конкретного контракта.Как WCF, я хотел бы иметь возможность написать интерфейс контракта, затем создать новый экземпляр класса say «ClientBase» с моим контрактом в качестве параметров шаблона, а затем использовать этот клиент в качестве «Удаленного» моего другого программного обеспечения.

В качестве примера всегда лучше вот что я хотел бы:

В совместном проекте:

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, но моя проблема заключается в создании экземпляра канала, потому что, конечно, у меня нет ни одного класса, который я мог бы создать.Мне нужен своего рода динамический класс, который реализует этот конкретный контракт и для каждого метода этого контракта обрабатывает сетевую логику для создания TcpClient, подключения его к удаленному серверу, отправки данных, ожидания ответа и возврата результата в ClientFooContract.

Конечно, я мог бы создать класс 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();
    }
}

Вот моя борьба, яне знаю, как добавить тело к моему динамическому методу, я нашел в интернете несколько примеров того, как создать локальную переменную и т. д., но на самом деле, как я могу создать TcpClient с IL, мне нужно поместить все это в отдельный методтогда вызвать этот метод?и как ?Как на самом деле я могу сделать простой вызов console.writeLine для фактического тестирования моей системы?

Моя проблема на самом деле заключается в создании MethodBody, потому что здесь мне придется проделать большую работу, а не просто объявить локальныйпеременная и обрабатывать основные операции.Если есть хотя бы способ вызвать другой метод в другом классе, это будет полезно

Если вы что-нибудь знаете, спасибо за вашу помощь

1 Ответ

0 голосов
/ 06 июня 2019

После нескольких часов тестирования я наконец-то нашел решение проблемы тела моего метода.

Вот это с комментарием (на тот случай, если кто-то однажды окажется в этой теме).

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