Как динамически объединить два интерфейса для перехода на RealProxy

При вызове базового конструктора RealProxy вы передаете тип целевого объекта для прокси. Я хотел бы динамически добавлять интерфейсы к прокси-типу, чтобы результирующий прокси-тип мог быть приведен к дополнительным интерфейсам.

Например:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main(string[] args)
        {
            MyProxy<IFoo> proxy = new MyProxy<IFoo>(new Foo());

            IFoo proxiedFoo = (IFoo)proxy.GetTransparentProxy();

            // make a proxied call...
            proxiedFoo.DoSomething();

            // cast proxiedFoo to IDisposable and dispose of it...
            IDisposable disposableFoo = proxiedFoo as IDisposable;

            // disposableFoo is null at this point.

            disposableFoo.Dispose();
        }
    }
}

public interface IFoo
{
    void DoSomething();
}

public class Foo : IFoo, IDisposable
{
    #region IFoo Members

    public void DoSomething()
    {
        //
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        // dispose
    }

    #endregion
}

public class MyProxy<T> : RealProxy where T : class
{
    private T _target;

    public MyProxy(T target) :
        base(CombineType(typeof(T), typeof(IDisposable)))
    {
        this._target = target;
    }

    private static Type CombineType(Type type1, Type type2)
    {
        // How to implement this method, Reflection.Emit????
        throw new NotImplementedException();
    }

    public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
    {
        return InvokeRemoteCall((IMethodCallMessage)msg, this._target);
    }

    /// <summary>
    /// Invokes the remote call.
    /// </summary>
    /// <param name="methodCall">The method call.</param>
    /// <param name="target">The target.</param>
    /// <returns>A <see cref="ReturnMessage"/></returns>
    private static IMessage InvokeRemoteCall(IMethodCallMessage methodCall, object target)
    {
        MethodInfo method = methodCall.MethodBase as MethodInfo;

        object callResult = (target != null) ? method.Invoke(target, methodCall.InArgs) : null;

        LogicalCallContext context = methodCall.LogicalCallContext;

        var query = method.GetParameters().Where(param => ((ParameterInfo)param).IsOut);

        ParameterInfo[] outParameters = query.ToArray();

        return new ReturnMessage(callResult, outParameters, outParameters.Count(), context, methodCall);
    }
}
}

Таким образом, чтобы иметь возможность разыграть прокси тип IDisposableМне нужно иметь возможность отправить IDisposable в дополнение к IFoo к RealProxy вызов базового конструктора.

По сути, как мне реализовать этот метод для динамического добавления IDisposable в IFoo быть прокси.

private static Type CombineType(Type type1, Type type2)
{
    // How to implement this method, Reflection.Emit????
    throw new NotImplementedException();
}

2 ответа

Решение

Я решил это. Вот полное решение с использованием Reflection Emit.

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main(string[] args)
        {
            MyProxy<IFoo> proxy = new MyProxy<IFoo>(new Foo());

            IFoo proxiedFoo = (IFoo)proxy.GetTransparentProxy();

            // make a proxied call...
            proxiedFoo.DoSomething();

            // cast proxiedFoo to IDisposable and dispose of it...
            IDisposable disposableFoo = proxiedFoo as IDisposable;

            // disposableFoo is null at this point.

            disposableFoo.Dispose();
        }
    }

    public interface IFoo
    {
        void DoSomething();
    }

    public class Foo : IFoo, IDisposable
    {
        #region IFoo Members

        public void DoSomething()
        {
            Console.WriteLine("DoSomething called!");
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            // dispose
            Console.WriteLine("Disposing Foo!");
        }

        #endregion
    }

    public class MyProxy<T> : RealProxy where T : class
    {
        private T _target;

        public MyProxy(T target) :
            base(CombineType(typeof(T), typeof(IDisposable)))
        {
            this._target = target;
        }

        private static Type CombineType(Type type1, Type type2)
        {
            // How to implement this method, Reflection.Emit????
            return DynamicInterfaceFactory.GenerateCombinedInterfaceType(type1, type2);
        }

        public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
        {
            return InvokeRemoteCall((IMethodCallMessage)msg, this._target);
        }

        /// <summary>
        /// Invokes the remote call.
        /// </summary>
        /// <param name="methodCall">The method call.</param>
        /// <param name="target">The target.</param>
        /// <returns>A <see cref="ReturnMessage"/></returns>
        private static IMessage InvokeRemoteCall(IMethodCallMessage methodCall, object target)
        {
            MethodInfo method = methodCall.MethodBase as MethodInfo;

            object callResult = (target != null) ? method.Invoke(target, methodCall.InArgs) : null;

            LogicalCallContext context = methodCall.LogicalCallContext;

            var query = method.GetParameters().Where(param => ((ParameterInfo)param).IsOut);

            ParameterInfo[] outParameters = query.ToArray();

            return new ReturnMessage(callResult, outParameters, outParameters.Count(), context, methodCall);
        }
    }

    public static class DynamicInterfaceFactory
    {
        public static Type GenerateCombinedInterfaceType(Type type1, Type type2)
        {            
            if (!type1.IsInterface)
                throw new ArgumentException("Type type1 is not an interface", "type1");

            if (!type2.IsInterface)
                throw new ArgumentException("Type type2 is not an interface", "type2");

            //////////////////////////////////////////////
            // Module and Assembly Creation

            var orginalAssemblyName = type1.Assembly.GetName().Name;

            ModuleBuilder moduleBuilder;

            var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());

            var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
                tempAssemblyName,
                System.Reflection.Emit.AssemblyBuilderAccess.RunAndCollect);

            moduleBuilder = dynamicAssembly.DefineDynamicModule(
                tempAssemblyName.Name,
                tempAssemblyName + ".dll");


            var assemblyName = moduleBuilder.Assembly.GetName();

            //////////////////////////////////////////////

            //////////////////////////////////////////////
            // Create the TypeBuilder

            var typeBuilder = moduleBuilder.DefineType(
                type1.FullName,
                TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);

            typeBuilder.AddInterfaceImplementation(type1);
            typeBuilder.AddInterfaceImplementation(type2);

            //////////////////////////////////////////////

            //////////////////////////////////////////////
            // Create and return the defined type

            Type newType = typeBuilder.CreateType();

            return newType;

            //////////////////////////////////////////////
        }
    }
}

Ключ должен был создать новый тип интерфейса, который является комбинацией двух передаваемых интерфейсов. Затем RealProxy может сопоставить новые методы динамического интерфейса с нашим методом MyProxy Invoke, к которому мы затем можем вызвать соответствующий метод.

Посмотрите на вызов сейчас CombineType:

private static Type CombineType(Type type1, Type type2)
{
    // How to implement this method, Reflection.Emit????
    return DynamicInterfaceFactory.GenerateCombinedInterfaceType(type1, type2);
}

Это тогда создает простой объединенный интерфейс в памяти.

public static class DynamicInterfaceFactory
{
    public static Type GenerateCombinedInterfaceType(Type type1, Type type2)
    {            
        if (!type1.IsInterface)
            throw new ArgumentException("Type type1 is not an interface", "type1");

        if (!type2.IsInterface)
            throw new ArgumentException("Type type2 is not an interface", "type2");

        //////////////////////////////////////////////
        // Module and Assembly Creation

        var orginalAssemblyName = type1.Assembly.GetName().Name;

        ModuleBuilder moduleBuilder;

        var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());

        var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            tempAssemblyName,
            System.Reflection.Emit.AssemblyBuilderAccess.RunAndCollect);

        moduleBuilder = dynamicAssembly.DefineDynamicModule(
            tempAssemblyName.Name,
            tempAssemblyName + ".dll");


        var assemblyName = moduleBuilder.Assembly.GetName();

        //////////////////////////////////////////////

        //////////////////////////////////////////////
        // Create the TypeBuilder

        var typeBuilder = moduleBuilder.DefineType(
            type1.FullName,
            TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);

        typeBuilder.AddInterfaceImplementation(type1);
        typeBuilder.AddInterfaceImplementation(type2);

        //////////////////////////////////////////////

        //////////////////////////////////////////////
        // Create and return the defined type

        Type newType = typeBuilder.CreateType();

        return newType;

        //////////////////////////////////////////////
    }
}

Который передается в RealProxy c'tor

public class MyProxy<T> : RealProxy where T : class
{
    private T _target;

    public MyProxy(T target) :
        base(CombineType(typeof(T), typeof(IDisposable)))
    {
        this._target = target;
    }

Вывод программы:

DoSomething called!
Disposing Foo!
Press any key to continue . . .

Это еще не пуленепробиваемый, но это стартер.

Есть очень простой, встроенный способ достижения этого. Тем не менее, это практически невозможно обнаружить, если вы еще не знаете об этом:-)

Для того, чтобы иметь возможность контролировать, какие операции приведения действительны для прозрачного прокси, связанного \ полученного из определенного RealProxy класс RealProxy необходимо реализовать дополнительный интерфейс, а именно IRemotingTypeInfo,

Один из методов, определенных IRemotingTypeInfo интерфейсы bool CanCastTo(Type type, object o), Этот метод вызывается каждый раз, когда делается попытка привести ваш прокси-объект к другому типу; "целевой" тип операции приведения доступен через type параметр.

Таким образом, чтобы ваш прокси "реализовал" несколько интерфейсов, просто верните true от CanCastTo() методы для типов, которые вы хотите поддерживать.

Обратите внимание, что после приведения вызовы метода на прозрачном прокси все еще принимаются тем же RealProxy пример.

Для более глубокого обсуждения вы можете прочитать эту статью MSDN: Создание пользовательской реализации маршалинга с использованием.NET Remoting и COM-взаимодействия

Вот полный пример:

interface IFaceA
{
    void MethodA();
}

interface IFaceB
{
    void MethodB();
}

class MultiFaceProxy : RealProxy, IRemotingTypeInfo
{
    public MultiFaceProxy()
        :base(typeof(IFaceA)) {}

    public bool CanCastTo(Type fromType, object o)
    {
        return fromType == typeof(IFaceA) || fromType == typeof(IFaceB);
    }

    public string TypeName
    {
        get { return GetProxiedType().FullName; }
        set { throw new NotSupportedException(); }
    }

    public override IMessage Invoke(IMessage msg)
    {
        // invoke logic
        return null;
    }
}

class Program
{
    static void Main(string[] args)
    {
        MultiFaceProxy proxy = new MultiFaceProxy();

        IFaceA ifa = (IFaceA) proxy.GetTransparentProxy();
        // The following now also works thanks to CanCastTo()
        IFaceB ifb = (IFaceB)ifa;
     }
}
Другие вопросы по тегам