简体   繁体   English

如何动态组合两个接口传递给RealProxy

[英]How to dynamically combine two interfaces to pass to RealProxy

In a call to the RealProxy base constructor, you pass the Type of the target object to be proxied. 在对RealProxy基础构造函数的调用中,传递要代理的目标对象的Type。 What I would like to do is dynamically add interfaces to the proxied type so that the resultant proxied type can be cast to the additional interfaces. 我想要做的是动态地将接口​​添加到代理类型,以便可以将结果代理类型强制转换为其他接口。

For example: 例如:

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

So in order to be able cast the proxied type to IDisposable , I need to be able to send IDisposable in addition to IFoo to the RealProxy base constructor call. 因此,为了能够将代理类型转换为IDisposable ,我需要能够将除IFoo之外的IDisposable发送到RealProxy基础构造函数调用。

In essence, how do I implement this method to dynamically add IDisposable to IFoo to be proxied. 实质上,我如何实现此方法以动态地将IDisposable添加到要代理的IFoo

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

There's a very simple, built-in way of achieving that. 有一种非常简单的内置方式来实现这一目标。 However, it is practically undiscoverable if one doesn't already know about it :-) 但是,如果一个人还不知道它,它实际上是不可发现的:-)

In order to be able to control what cast operations are valid on the transparent proxy associated\\obtained from a certain RealProxy -derived class the RealProxy needs to implement an additional interface, namely IRemotingTypeInfo . 为了能够控制哪些强制转换操作在从某个RealProxy派生的类获得的透明代理上有效, RealProxy需要实现一个额外的接口,即IRemotingTypeInfo

One of the methods defined by the IRemotingTypeInfo interfaces is bool CanCastTo(Type type, object o) . IRemotingTypeInfo接口定义的方法IRemotingTypeInfobool CanCastTo(Type type, object o) This method is called every time an attempt is made to cast your proxy object to some other type; 每次尝试将代理对象转换为其他类型时,都会调用此方法; the "target" type of the cast operation is accessible via the type parameter. 可以通过type参数访问强制转换操作的“目标”类型。

So, to allow your proxy to "implement" multiple interfaces just return true from the CanCastTo() methods for the types you wish to support. 因此,为了让您的代理“实现”多个接口,只需从您希望支持的类型的CanCastTo()方法返回true

Please note that after the cast the method calls on the transparent proxy are still received by the same RealProxy instance. 请注意,在强制转换后,透明代理上的方法调用仍然由同一个RealProxy实例接收。

For a more in-depth discussion you might read this MSDN article: Create a Custom Marshaling Implementation Using .NET Remoting and COM Interop 有关更深入的讨论,您可以阅读此MSDN文章: 使用.NET Remoting和COM Interop创建自定义封送实现

Here's a complete example: 这是一个完整的例子:

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

I solved it. 我解决了 Here is the full solution using Reflection Emit. 以下是使用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;

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

The key was to create a new interface type that is a combination of the two interfaces passed in. Then RealProxy can map the new dynamic interface methods to our MyProxy Invoke method to which we then can invoke the appropriate method. 关键是要创建一个新接口类型,它是传入的两个接口的组合。然后,RealProxy可以将新的动态接口方法映射到MyProxy Invoke方法,然后我们可以调用适当的方法。

Look at the call now to CombineType: 现在看看CombineType的调用:

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

That then creates a simple in-memory combined interface. 然后创建一个简单的内存组合接口。

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;

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

Which is passed to the RealProxy c'tor 哪个传递给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;
    }

Output of program: 计划产出:

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

This is not bulletproof yet but is a starter. 这不是防弹,但它是一个先发制人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM