Как динамически объединить два интерфейса для перехода на 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;
}
}