C# 用C语言在运行时为接口创建类#

C# 用C语言在运行时为接口创建类#,c#,code-generation,multicast,reflection.emit,C#,Code Generation,Multicast,Reflection.emit,我正在研究一组对象,假设目前有3个对象处于活动状态,它们都实现了一个公共接口,然后将这些对象包装到第四个对象中,也实现了相同的接口 第四个对象的方法和属性实现只需调用这3个底层对象上的相关位。我知道在某些情况下这样做是没有意义的,但这是针对服务多播体系结构的,因此已经有一组很好的限制 我的问题是从哪里开始。第四个对象的生成应该在运行时在内存中完成,所以我想Reflection.Emit,不幸的是,我没有足够的经验,甚至不知道从哪里开始 我必须构造内存中的程序集吗?看起来确实如此,但我只是想快速指

我正在研究一组对象,假设目前有3个对象处于活动状态,它们都实现了一个公共接口,然后将这些对象包装到第四个对象中,也实现了相同的接口

第四个对象的方法和属性实现只需调用这3个底层对象上的相关位。我知道在某些情况下这样做是没有意义的,但这是针对服务多播体系结构的,因此已经有一组很好的限制

我的问题是从哪里开始。第四个对象的生成应该在运行时在内存中完成,所以我想
Reflection.Emit
,不幸的是,我没有足够的经验,甚至不知道从哪里开始

我必须构造内存中的程序集吗?看起来确实如此,但我只是想快速指出我应该从哪里开始

基本上,我在看一个接口和一个对象实例列表,所有这些实例都实现了该接口,并构造了一个新的对象,也实现了该接口,它应该“多播”所有方法调用和对所有底层对象的属性访问,至少尽可能多。会有大量的问题和例外,但我会处理这些位,当我得到他们

这是一个面向服务的体系结构,我希望现有的代码以一个记录器服务为例,现在可以访问多个记录器服务,而无需更改使用这些服务的代码。相反,我希望在运行时生成一个logger服务包装器,该包装器在内部仅调用多个底层对象上的相关方法


这是针对.NET 3.5和C#的。您真的需要在运行时创建程序集吗

也许你不需要它

c#给你行动,is操作符和lambda/委托…

(我在这里通过添加额外的上下文/信息来证明答案的合理性)

是的,目前反射。发射是解决这个问题的唯一方法

在.NET4.0中,
表达式
类已扩展为支持循环和语句块,因此对于单个方法的使用,编译的
表达式
将是一个好主意。但即使这样也不支持多方法接口(仅支持单个方法委托)


幸运的是,我以前做过这件事;请参见

如果有人感兴趣,我将在这里发布我自己的实现

这是受马克的答案影响很大的,我接受了他的答案

代码可以用来包装一组对象,它们都实现了一个公共接口,在一个新对象中,也实现了所述接口。当访问返回对象上的方法和属性时,底层对象上相应的方法和属性将以相同的方式访问

这里有龙:这是用于特定用途的。这可能会产生一些奇怪的问题,特别是因为代码不能确保所有底层对象都被赋予与被调用方传递的对象完全相同的对象(或者更确切地说,它不禁止底层对象中的一个与参数混淆),对于返回值的方法,只返回最后一个返回值。至于out/ref参数,我甚至没有测试它是如何工作的,但它可能没有您已收到警告。

#region Using

using System;
using System.Linq;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using LVK.Collections;

#endregion

namespace LVK.IoC
{
    /// <summary>
    /// This class implements a service wrapper that can wrap multiple services into a single multicast
    /// service, that will in turn dispatch all method calls down into all the underlying services.
    /// </summary>
    /// <remarks>
    /// This code is heavily influenced and copied from Marc Gravell's implementation which he
    /// posted on Stack Overflow here: http://stackoverflow.com/questions/847809
    /// </remarks>
    public static class MulticastService
    {
        /// <summary>
        /// Wrap the specified services in a single multicast service object.
        /// </summary>
        /// <typeparam name="TService">
        /// The type of service to implement a multicast service for.
        /// </typeparam>
        /// <param name="services">
        /// The underlying service objects to multicast all method calls to.
        /// </param>
        /// <returns>
        /// The multicast service instance.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="services"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="services"/> contains a <c>null</c> reference.</para>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <para><typeparamref name="TService"/> is not an interface type.</para>
        /// </exception>
        public static TService Wrap<TService>(params TService[] services)
            where TService: class
        {
            return (TService)Wrap(typeof(TService), (Object[])services);
        }

        /// <summary>
        /// Wrap the specified services in a single multicast service object.
        /// </summary>
        /// <param name="serviceInterfaceType">
        /// The <see cref="Type"/> object for the service interface to implement a multicast service for.
        /// </param>
        /// <param name="services">
        /// The underlying service objects to multicast all method calls to.
        /// </param>
        /// <returns>
        /// The multicast service instance.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="serviceInterfaceType"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="services"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="services"/> contains a <c>null</c> reference.</para>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <para><typeparamref name="TService"/> is not an interface type.</para>
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// <para>One or more of the service objects in <paramref name="services"/> does not implement
        /// the <paramref name="serviceInterfaceType"/> interface.</para>
        /// </exception>
        public static Object Wrap(Type serviceInterfaceType, params Object[] services)
        {
            #region Parameter Validation

            if (Object.ReferenceEquals(null, serviceInterfaceType))
                throw new ArgumentNullException("serviceInterfaceType");
            if (!serviceInterfaceType.IsInterface)
                throw new ArgumentException("serviceInterfaceType");
            if (Object.ReferenceEquals(null, services) || services.Length == 0)
                throw new ArgumentNullException("services");
            foreach (var service in services)
            {
                if (Object.ReferenceEquals(null, service))
                    throw new ArgumentNullException("services");
                if (!serviceInterfaceType.IsAssignableFrom(service.GetType()))
                    throw new InvalidOperationException("One of the specified services does not implement the specified service interface");
            }

            #endregion

            if (services.Length == 1)
                return services[0];

            AssemblyName assemblyName = new AssemblyName(String.Format("tmp_{0}", serviceInterfaceType.FullName));
            String moduleName = String.Format("{0}.dll", assemblyName.Name);
            String ns = serviceInterfaceType.Namespace;
            if (!String.IsNullOrEmpty(ns))
                ns += ".";

            var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
                AssemblyBuilderAccess.RunAndSave);
            var module = assembly.DefineDynamicModule(moduleName, false);
            var type = module.DefineType(String.Format("{0}Multicast_{1}", ns, serviceInterfaceType.Name),
                TypeAttributes.Class |
                TypeAttributes.AnsiClass |
                TypeAttributes.Sealed |
                TypeAttributes.NotPublic);
            type.AddInterfaceImplementation(serviceInterfaceType);

            var ar = Array.CreateInstance(serviceInterfaceType, services.Length);
            for (Int32 index = 0; index < services.Length; index++)
                ar.SetValue(services[index], index);

            // Define _Service0..N-1 private service fields
            FieldBuilder[] fields = new FieldBuilder[services.Length];
            var cab = new CustomAttributeBuilder(
                typeof(DebuggerBrowsableAttribute).GetConstructor(new Type[] { typeof(DebuggerBrowsableState) }),
                new Object[] { DebuggerBrowsableState.Never });
            for (Int32 index = 0; index < services.Length; index++)
            {
                fields[index] = type.DefineField(String.Format("_Service{0}", index),
                    serviceInterfaceType, FieldAttributes.Private);

                // Ensure the field don't show up in the debugger tooltips
                fields[index].SetCustomAttribute(cab);
            }

            // Define a simple constructor that takes all our services as arguments
            var ctor = type.DefineConstructor(MethodAttributes.Public,
                CallingConventions.HasThis,
                Sequences.Repeat(serviceInterfaceType, services.Length).ToArray());
            var generator = ctor.GetILGenerator();

            // Store each service into its own fields
            for (Int32 index = 0; index < services.Length; index++)
            {
                generator.Emit(OpCodes.Ldarg_0);
                switch (index)
                {
                    case 0:
                        generator.Emit(OpCodes.Ldarg_1);
                        break;

                    case 1:
                        generator.Emit(OpCodes.Ldarg_2);
                        break;

                    case 2:
                        generator.Emit(OpCodes.Ldarg_3);
                        break;

                    default:
                        generator.Emit(OpCodes.Ldarg, index + 1);
                        break;
                }
                generator.Emit(OpCodes.Stfld, fields[index]);
            }
            generator.Emit(OpCodes.Ret);

            // Implement all the methods of the interface
            foreach (var method in serviceInterfaceType.GetMethods())
            {
                var args = method.GetParameters();
                var methodImpl = type.DefineMethod(method.Name,
                    MethodAttributes.Private | MethodAttributes.Virtual,
                    method.ReturnType, (from arg in args select arg.ParameterType).ToArray());
                type.DefineMethodOverride(methodImpl, method);

                // Generate code to simply call down into each service object
                // Any return values are discarded, except the last one, which is returned
                generator = methodImpl.GetILGenerator();
                for (Int32 index = 0; index < services.Length; index++)
                {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldfld, fields[index]);
                    for (Int32 paramIndex = 0; paramIndex < args.Length; paramIndex++)
                    {
                        switch (paramIndex)
                        {
                            case 0:
                                generator.Emit(OpCodes.Ldarg_1);
                                break;

                            case 1:
                                generator.Emit(OpCodes.Ldarg_2);
                                break;

                            case 2:
                                generator.Emit(OpCodes.Ldarg_3);
                                break;

                            default:
                                generator.Emit((paramIndex < 255)
                                    ? OpCodes.Ldarg_S
                                    : OpCodes.Ldarg,
                                    paramIndex + 1);
                                break;
                        }

                    }
                    generator.Emit(OpCodes.Callvirt, method);
                    if (method.ReturnType != typeof(void) && index < services.Length - 1)
                        generator.Emit(OpCodes.Pop); // discard N-1 return values
                }
                generator.Emit(OpCodes.Ret);
            }

            return Activator.CreateInstance(type.CreateType(), services);
        }
    }
}
#区域使用
使用制度;
使用System.Linq;
使用系统诊断;
运用系统反思;
使用System.Reflection.Emit;
使用LVK.Collections;
#端区
名称空间LVK.IoC
{
/// 
///此类实现了一个服务包装器,可以将多个服务包装成一个多播
///服务,从而将所有方法调用向下分派到所有底层服务中。
/// 
/// 
///这段代码深受Marc Gravell实现的影响,并抄袭了他的实现
///在堆栈溢出上发布如下:http://stackoverflow.com/questions/847809
/// 
公共静态类多播服务
{
/// 
///将指定的服务包装在单个多播服务对象中。
/// 
/// 
///要为其实现多播服务的服务类型。
/// 
/// 
///要将所有方法调用多播到的基础服务对象。
/// 
/// 
///多播服务实例。
/// 
/// 
///是空的。
///-或-
///包含空引用。
/// 
/// 
///不是接口类型。
/// 
公共静态TService Wrap(params TService[]服务)
where-TService:class
{
返回(TService)包装(typeof(TService),(Object[])服务);
}
/// 
///将指定的服务包装在单个多播服务对象中。
/// 
/// 
///服务接口用于为实现多播服务的对象。
/// 
/// 
///要将所有方法调用多播到的基础服务对象。
/// 
/// 
///多播服务实例。
/// 
/// 
///是空的。
///-或-
///是空的。
///-或-
///包含空引用。
/// 
/// 
///不是接口类型。
/// 
/// 
///中的一个或多个服务对象未实现
///界面。
/// 
公共静态对象包装(类型serviceInterfaceType,参数对象[]服务)
{
#区域参数验证
if(Object.ReferenceEquals(null,serviceInterfaceType))
抛出新ArgumentNullException(“serviceInterfaceType”);
如果(!serviceInterfaceType.IsInterface)
抛出新ArgumentException(“serviceInterfaceType”)