2013-08-20 6 views
3

При вызове базового конструктора 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(); 
} 
+0

Я думаю, что вы не можете реализовать свой 'CombineType()' в пути это будет иметь смысл здесь. – svick

+0

@svick - Думаю, я мог бы использовать отражение Emit для создания нового динамического типа в памяти, который объединяет два целевых интерфейса, и они должны быть интерфейсами. – Jim

ответ

1

Я решил. Вот полное решение с использованием 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; 

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

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

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

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

+1

Поскольку вы не хотите сохранять сгенерированную сборку, я думаю, вы должны использовать 'AssemblyBuilderAccess.RunAndCollect', чтобы сборки не просачивались. – svick

+0

@svick - спасибо за подсказку, я изменю его. – Jim

6

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

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

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

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

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

Для более углубленного обсуждения вы можете прочитать эту статью MSDN: Create a Custom Marshaling Implementation Using .NET Remoting and COM Interop

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

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; 
    } 
} 
+0

Спасибо, ты спас мой день. – Dennis