You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET依赖注入中注入带默认实现的接口?

带默认实现接口的依赖注入问题

问题场景

我有一个包含默认实现方法的接口:

public interface ITestService
{
    string GetName(string name) => $"{name} coming from interface";
}

尝试直接将其注入DI容器时:

services.AddScoped<ITestService>();

运行时抛出错误:

System.ArgumentException: 'Cannot instantiate implementation type 'ITestService' for service type 'ITestService'.'

手动编写空实现类后可以正常注入:

public class TestService : ITestService { }

// 注册
services.AddScoped<ITestService, TestService>();

解决方案

接口本身无法被实例化,DI容器必须绑定到具体实现类型,但可以通过以下方式避免手动编写空实现类:

1. 动态生成实现类(通用方案)

通过反射Emit动态创建一个实现目标接口的空类,然后注册到容器:

using System.Reflection;
using System.Reflection.Emit;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddDefaultInterfaceImpl<TInterface>(this IServiceCollection services, ServiceLifetime lifetime)
        where TInterface : class
    {
        if (!typeof(TInterface).IsInterface)
            throw new ArgumentException("泛型参数必须是接口类型", nameof(TInterface));

        // 构建动态程序集与类型
        var assemblyName = new AssemblyName($"Dynamic_{typeof(TInterface).Name}Impl");
        var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
        var typeBuilder = moduleBuilder.DefineType(
            $"Dynamic_{typeof(TInterface).Name}",
            TypeAttributes.Public | TypeAttributes.Class,
            null,
            new[] { typeof(TInterface) });

        // 添加无参构造函数
        typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

        // 创建类型并注册
        var implType = typeBuilder.CreateType();
        switch (lifetime)
        {
            case ServiceLifetime.Scoped:
                services.AddScoped(typeof(TInterface), implType);
                break;
            case ServiceLifetime.Singleton:
                services.AddSingleton(typeof(TInterface), implType);
                break;
            case ServiceLifetime.Transient:
                services.AddTransient(typeof(TInterface), implType);
                break;
        }

        return services;
    }
}

使用方式:

services.AddDefaultInterfaceImpl<ITestService>(ServiceLifetime.Scoped);

2. 直接使用工厂委托(简化方案)

如果只是针对单个接口,也可以直接在注册时通过工厂委托创建动态实例:

services.AddScoped<ITestService>(sp => 
{
    var type = typeof(ITestService);
    var assemblyName = new AssemblyName($"Temp_{type.Name}");
    var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
    var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
    var typeBuilder = moduleBuilder.DefineType($"Temp_{type.Name}Impl", TypeAttributes.Public | TypeAttributes.Class, null, new[] { type });
    typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
    var implType = typeBuilder.CreateType();
    return (ITestService)Activator.CreateInstance(implType);
});

说明

接口的默认方法只是提供了默认逻辑,但接口本身依然不能被实例化,必须依赖具体的实现类型。上述方案本质都是自动生成空实现类,从而满足DI容器的实例化要求。

内容的提问来源于stack exchange,提问作者sa-es-ir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:33:18