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

