在C#中为运行时的接口创建一个类
我正在考虑使用一组对象,假设此时有3个对象存活,它们都实现了一个公共接口,然后将这些对象包装在第四个对象中,同时实现相同的接口。
第四个对象的方法和属性的实现将简单地调用这3个底层对象上的相关位。 我知道这里会出现这样的情况,这样做是没有意义的,但这是针对服务多播体系结构的,因此已经有了一套很好的限制。
我的问题是从哪里开始。 第四个对象的生成应该在内存中,在运行时完成,所以我在想Reflection.Emit
,遗憾的是我没有足够的经验来知道从哪里开始。
我是否必须构建内存中的程序集? 它看起来确实如此,但我只想快速指向我应该从哪里开始。
基本上我正在寻找一个接口,以及一个实现该接口的对象实例列表,并构建一个新对象,同时实现该接口,该接口应该“多播”所有方法调用和对所有底层对象的属性访问,at尽可能少。 exception会有很多问题,但是当我到达它们时我会解决这些问题。
这适用于面向服务的体系结构,我希望现有的代码(例如,记录器服务)现在可以访问多个记录器服务,而无需更改使用服务的代码。 相反,我想运行时生成一个logger-service-wrapper,它在内部简单地调用多个底层对象上的相关方法。
这适用于.NET 3.5和C#。
(我在这里通过添加额外的上下文/信息来certificate答案)
是的,目前Reflection.Emit
是解决这个问题的唯一方法。
在.NET 4.0中, Expression
类已经扩展为支持循环和语句块,因此对于单个方法的使用,编译的Expression
是个好主意。 但即使这样也不支持多方法接口(只是单方法委托)。
幸运的是,我以前做过这件事; 请参阅如何编写在C#中实现给定接口的通用容器类?
如果有人感兴趣,我会在这里发布我自己的实现。
这受到Marc的回答的影响和复制,我接受了。
该代码可用于包装一组对象,所有对象都实现了一个公共接口,在一个新对象中,也实现了所述接口。 访问返回对象上的方法和属性时,将以相同方式访问基础对象上的相应方法和属性。
这里有龙 :这是用于特定用途。 这可能存在奇怪的问题,特别是因为代码不能确保给所有底层对象提供与被调用者传递的完全相同的对象(或者更确切地说,它不会禁止其中一个底层对象搞乱参数) ,对于返回值的方法,只返回最后一个返回值。 至于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 { /// /// 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. /// /// /// This code is heavily influenced and copied from Marc Gravell's implementation which he /// posted on Stack Overflow here: http://stackoverflow.com/questions/847809 /// public static class MulticastService { /// /// Wrap the specified services in a single multicast service object. /// /// /// The type of service to implement a multicast service for. /// /// /// The underlying service objects to multicast all method calls to. /// /// /// The multicast service instance. /// /// /// is null . /// - or - /// contains a null reference. /// /// /// is not an interface type. /// public static TService Wrap(params TService[] services) where TService: class { return (TService)Wrap(typeof(TService), (Object[])services); } /// /// Wrap the specified services in a single multicast service object. /// /// /// The object for the service interface to implement a multicast service for. /// /// /// The underlying service objects to multicast all method calls to. /// /// /// The multicast service instance. /// /// /// is null . /// - or - /// is null . /// - or - /// contains a null reference. /// /// /// is not an interface type. /// /// /// One or more of the service objects in does not implement /// the interface. /// 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); } } }
你真的需要在运行时创建程序集吗?
可能你不需要它。
c#给你Action