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

C#依赖注入中使用不同约束实现类的TypeLoadException问题

问题:依赖注入中创建IFoo<Bar>实例时抛出TypeLoadException

实例e可以正常创建,包含Foo1和Foo2;但创建实例c和d时抛出TypeLoadException。

我认为在这两个类中(一个依赖IEnumerable<IFoo<Bar>>)应该可以正常创建Foo1,不是吗?或者我需要在ServiceCollection中编写选择机制?

internal class Program
{
    private static void Main(string[] args)
    {
        var sc = new ServiceCollection();
        sc.AddSingleton(typeof(IFoo<>), typeof(Foo1<>));
        sc.AddSingleton(typeof(IFoo<>), typeof(Foo2<>));
        sc.AddSingleton<C>();
        sc.AddSingleton<D>();
        sc.AddSingleton<E>();

        var sp = sc.BuildServiceProvider();

        var e = sp.GetRequiredService<E>();
        var c = sp.GetRequiredService<C>();
        var d = sp.GetRequiredService<D>();
        
    }
}

public class C
{
    public IFoo<Bar>[] Foos { get; }

    public C(IEnumerable<IFoo<Bar>> foos)
    {
        Foos = foos.ToArray();
    }
}

public class D
{
    public IFoo<Bar> Foo { get; }

    public D(IFoo<Bar> foo)
    {
        Foo = foo;
    }
}

public class E
{
    public IFoo<BarEq>[] Foos { get; }

    public E(IEnumerable<IFoo<BarEq>> foos)
    {
        Foos = foos.ToArray();
    }
}

public class Bar(int I);

public record BarEq(int I);

public interface IFoo<T> { }

public sealed class Foo1<T> : IFoo<T> { }

public sealed class Foo2<T> : IFoo<T> where T : IEquatable<T> { }

抛出的异常信息:

System.TypeLoadException: 'GenericArguments[0], 'Bar', on 'Foo2`1[T]' violates the constraint of type parameter 'T'.

堆栈跟踪:

en System.RuntimeTypeHandle.Instantiate(RuntimeType inst) en /_/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs: línea 540
en System.RuntimeType.MakeGenericType(Type[] instantiation) en /_/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs: línea 3522

.csproj文件内容:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <AssemblyVersion>1.0</AssemblyVersion>
        <FileVersion>1.0.23.0</FileVersion>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
      <PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
    </ItemGroup>

</Project>

原因与解决方案

问题原因

DI容器解析IFoo<Bar>时,会尝试实例化所有注册的实现类型:Foo1<Bar>和Foo2<Bar>。但Foo2<T>有泛型约束where T : IEquatable<T>,而Bar类未实现IEquatable<Bar>接口,导致容器创建Foo2<Bar>时触发TypeLoadException。

实例E能正常创建,是因为BarEq是record类型,默认实现了IEquatable<BarEq>,满足Foo2<T>的约束,因此Foo1<BarEq>和Foo2<BarEq>都能被正常实例化。

解决方案

方案1:让Bar实现IEquatable<Bar>接口

修改Bar类定义,实现约束要求的接口,使Foo2<Bar>能正常实例化:

public class Bar(int i) : IEquatable<Bar>
{
    public int I { get; } = i;

    public bool Equals(Bar? other)
    {
        if (other is null) return false;
        return I == other.I;
    }

    public override bool Equals(object? obj) => Equals(obj as Bar);

    public override int GetHashCode() => I.GetHashCode();
}

方案2:为Foo2<>添加注册条件

通过工厂方法判断泛型参数是否满足约束,仅在符合条件时创建Foo2<>实例:

sc.AddSingleton(typeof(IFoo<>), typeof(Foo1<>));
sc.AddSingleton(typeof(IFoo<>), sp =>
{
    var currentServiceType = sp.GetRequiredKeyedService<Type>(typeof(IFoo<>));
    var genericArg = currentServiceType.GetGenericArguments()[0];
    var equatableType = typeof(IEquatable<>).MakeGenericType(genericArg);
    
    if (equatableType.IsAssignableFrom(genericArg))
    {
        var foo2Type = typeof(Foo2<>).MakeGenericType(genericArg);
        return Activator.CreateInstance(foo2Type)!;
    }
    return null;
});

此方式下,解析IFoo<Bar>时仅实例化Foo1<Bar>;解析IFoo<BarEq>时同时实例化两种实现。

方案3:针对特定泛型参数单独注册

如果仅需在特定类型上使用Foo2<>,可直接单独注册:

sc.AddSingleton(typeof(IFoo<>), typeof(Foo1<>));
// 仅为BarEq注册Foo2<>
sc.AddSingleton(typeof(IFoo<BarEq>), typeof(Foo2<BarEq>));

内容的提问来源于stack exchange,提问作者DrkDeveloper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 08:54:49