C# 如何通过反射将对象强制转换为其隐式实现的接口类型
C# 运行时不支持为已创建的实例动态追加接口实现,你可以通过适配器或动态代理的方式包装现有 Mock 实例,上层业务代码无需任何修改即可正常使用,以下是两种常用实现方案:
方案1:手写适配器(无额外依赖,适合少量标记接口场景)
仅需新增一个适配器类,把所有接口方法转发到内部的 MockRepository<Email> 实例即可:
public class EmailRepositoryAdapter : IEmailRepository { private readonly IRepository<Email> _innerRepository; public EmailRepositoryAdapter(IRepository<Email> innerRepository) { _innerRepository = innerRepository; } // 转发所有IRepository<Email>定义的方法 public void Save(Email entity) => _innerRepository.Save(entity); }
使用方式:
MockRepository<Email> mockRepository = CreateMockRepository<Email>(); IEmailRepository mockedEmailRepository = new EmailRepositoryAdapter(mockRepository); SomeBusinessMethod(mockedEmailRepository);
方案2:运行时动态生成通用适配器(适合大量同类标记接口场景)
如果项目中存在大量类似的空标记仓储接口,可以用反射发射实现通用适配逻辑,避免逐个编写适配器类:
using System.Reflection; using System.Reflection.Emit; public static class RepositoryAdapterGenerator { public static TInterface Adapt<TInterface, TEntity>(IRepository<TEntity> innerRepository) where TInterface : IRepository<TEntity> { // 定义动态程序集和模块 var assemblyName = new AssemblyName($"DynamicAdapter_{Guid.NewGuid():N}"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); // 定义适配器类 var typeName = $"{typeof(TInterface).Name}_Adapter"; var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class); // 声明内部仓储字段 var innerField = typeBuilder.DefineField("_inner", typeof(IRepository<TEntity>), FieldAttributes.Private); // 实现构造函数,注入内部仓储实例 var ctorBuilder = typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IRepository<TEntity>) }); var ilGenerator = ctorBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldarg_1); ilGenerator.Emit(OpCodes.Stfld, innerField); ilGenerator.Emit(OpCodes.Ret); // 实现目标标记接口 typeBuilder.AddInterfaceImplementation(typeof(TInterface)); // 转发所有接口方法到内部仓储实例 var interfaceMethods = typeof(TInterface) .GetInterfaces() .Concat(new[] { typeof(TInterface) }) .SelectMany(x => x.GetMethods()); foreach (var interfaceMethod in interfaceMethods) { var parameters = interfaceMethod.GetParameters(); var methodBuilder = typeBuilder.DefineMethod( interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, interfaceMethod.ReturnType, parameters.Select(p => p.ParameterType).ToArray()); var methodIl = methodBuilder.GetILGenerator(); methodIl.Emit(OpCodes.Ldarg_0); methodIl.Emit(OpCodes.Ldfld, innerField); for (var i = 0; i < parameters.Length; i++) { methodIl.Emit(OpCodes.Ldarg, i + 1); } methodIl.Emit(OpCodes.Callvirt, interfaceMethod); methodIl.Emit(OpCodes.Ret); typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethod); } // 创建实例并返回 var adapterType = typeBuilder.CreateType(); return (TInterface)Activator.CreateInstance(adapterType, innerRepository); } }
使用方式:
MockRepository<Email> mockRepository = CreateMockRepository<Email>(); IEmailRepository mockedEmailRepository = RepositoryAdapterGenerator.Adapt<IEmailRepository, Email>(mockRepository); SomeBusinessMethod(mockedEmailRepository);
两种方案都不需要修改现有业务逻辑代码,仅需在模拟仓储注入的环节做一层包装即可。如果使用依赖注入容器,可直接在服务注册逻辑中完成适配,业务侧完全无感知。
内容的提问来源于stack exchange,提问作者Tvde1
相关产品推荐
相关产品推荐

