gpt4 book ai didi

c# - 在运行时为接口(interface)创建类,在 C# 中

转载 作者:可可西里 更新时间:2023-11-01 09:06:00 43 4
gpt4 key购买 nike

我正在考虑获取一组对象,假设目前有 3 个对象存在,它们都实现了一个公共(public)接口(interface),然后将这些对象包装在第四个对象中,也实现了相同的接口(interface)。

第四个对象的方法和属性的实现将简单地调用这 3 个底层对象的相关位。我知道这里会有这样做没有意义的情况,但这是针对服务多播架构的,因此已经存在一系列很好的限制。

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

我是否必须构建内存中程序集?它确实看起来是这样,但我只是想要一个快速指向我应该从哪里开始的指针。

基本上,我正在寻找一个接口(interface),以及所有实现该接口(interface)的对象实例列表,并构造一个新对象,也实现该接口(interface),它应该“多播”所有方法调用和对所有底层的属性访问对象,至少尽可能多。会有很多异常等问题,但我会在遇到这些问题时解决这些问题。

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

这适用于 .NET 3.5 和 C#。

最佳答案

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

这在很大程度上受到了 Marc 的回答的影响和复制,我接受了该回答。

代码可用于包装一组对象,所有对象都实现一个公共(public)接口(interface),在一个新对象内,也实现所述接口(interface)。当访问返回对象的方法和属性时,底层对象的相应方法和属性将以相同的方式被访问。

这里有龙:这是为了特定的用途。这可能会出现奇怪的问题,特别是因为代码不能确保所有底层对象都被赋予与被调用者传递的对象完全相同的对象(或者更确切地说,它不禁止底层对象之一弄乱参数) , 对于有返回值的方法,只会返回最后一个返回值。至于 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);
}
}
}

关于c# - 在运行时为接口(interface)创建类,在 C# 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1205518/

43 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com