手撕代码:C#.NET实现自定义IOC容器
一、概要
手撕IOC容器的代码实现,下面是一个基于C#实现的功能强大的IOC容器示例。这个容器将包括服务的注册与注入、单例模式、工厂方法、构造函数注入、属性注入、方法注入、自动装配、生命周期管理和注解支持等功能。
二、实现
1、基础设施
首先需要定义一些基础设施,包括服务生命周期枚举、服务描述类等。
public class IocContainerClass
{public enum MyIocServiceLifetime{Singleton,Transient,Scoped}public class MyIocServiceDescriptor{public Type ServiceType { get; }public Type ImplementationType { get; }public object? ImplementationInstance { get; set; }public MyIocServiceLifetime Lifetime { get; }public MyIocServiceDescriptor(Type serviceType, Type implementationType, MyIocServiceLifetime lifetime) { ServiceType = serviceType;ImplementationType = implementationType;Lifetime = lifetime;}}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public class MyIocInjectAttribute : Attribute
{
}
Singleton:单例生命周期
Transient:瞬时生命周期
Scoped:作用域生命周期
2、服务注册
接下来需要一个容器来管理服务的注册和解析。
public class MyIocServiceCollection{private readonly List<MyIocServiceDescriptor> _services = new List<MyIocServiceDescriptor>();public void AddSingleton<TService,TImplementation>() where TService: class where TImplementation :class, TService{_services.Add(new MyIocServiceDescriptor(typeof(TService),typeof(TImplementation), MyIocServiceLifetime.Singleton));}public void AddTransient<TService,TImplementation>() where TService : class where TImplementation : class, TService{_services.Add(new MyIocServiceDescriptor(typeof(TService),typeof(TImplementation), MyIocServiceLifetime.Transient));}public void AddScoped<TService,TImplementation>() where TService : class where TImplementation : class, TService{_services.Add(new MyIocServiceDescriptor(typeof(TService), typeof(TImplementation), MyIocServiceLifetime.Scoped));}public MyServiceProvider BuildServiceProvider(){return new MyServiceProvider(_services);}}
3、服务获取解析
这里需要定义一个服务提供者,用于解析已注册的服务返回等。
public class MyServiceProvider : IDisposable
{private readonly Dictionary<Type, MyIocServiceDescriptor> _serviceDescriptor;private readonly Dictionary<Type, object?> _scopedInstances = new Dictionary<Type, object?>();private readonly bool _isRootProvider;public MyServiceProvider(List<MyIocServiceDescriptor> descriptors) {_serviceDescriptor = descriptors.ToDictionary(s => s.ServiceType, s => s);_isRootProvider = true;}private MyServiceProvider(Dictionary<Type, MyIocServiceDescriptor> serviceDescriptors){_serviceDescriptor = serviceDescriptors;_isRootProvider = false;}public MyServiceProvider CreateScope(){return new MyServiceProvider(_serviceDescriptor);}public object? GetService(Type serviceType){if(_serviceDescriptor.TryGetValue(serviceType, out var serviceDescriptor)){//单例生命周期if(serviceDescriptor.Lifetime == MyIocServiceLifetime.Singleton) { if(serviceDescriptor.ImplementationInstance == null){serviceDescriptor.ImplementationInstance = CreateInstance(serviceDescriptor.ImplementationType);}return serviceDescriptor.ImplementationInstance;}//作用域生命周期if(serviceDescriptor.Lifetime == MyIocServiceLifetime.Scoped){if (!_scopedInstances.ContainsKey(serviceType)){_scopedInstances[serviceType] = CreateInstance(serviceDescriptor.ImplementationType);}return _scopedInstances[serviceType];}//瞬时生命周期return CreateInstance(serviceDescriptor.ImplementationType);}else{throw new Exception($"Service of type {serviceType.Name} is not registered.");}}public T? GetService<T>(){return (T?)GetService(typeof(T?));}public void Dispose(){if (!_isRootProvider){foreach (var scopedInstance in _scopedInstances.Values){if (scopedInstance is IDisposable disposable){disposable.Dispose();}}}}
4、依赖注入
使用反射创建实例,实现构造函数注入、方法注入和属性注入等。
public object? CreateInstance(Type type)
{#region 构造函数注入// 获取所有公共构造函数var constructors = type.GetConstructors();// 按参数数量降序排列,选择参数最多的构造函数var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).First();// 获取构造函数的参数var parameters = constructor.GetParameters().Select(p => GetService(p.ParameterType)).ToArray();var instance = Activator.CreateInstance(type, parameters);#endregion//属性注入foreach (var property in type.GetProperties().Where(s => s.CanWrite &&s.GetCustomAttributes(typeof(MyIocInjectAttribute), true).Any())){if (_serviceDescriptor.ContainsKey(property.PropertyType)){property.SetValue(instance, GetService(property.PropertyType));}}//方法注入foreach (var method in type.GetMethods().Where(m=>m.IsPublic && m.ReturnType == typeof(void) &&m.GetCustomAttributes(typeof(MyIocInjectAttribute), true).Any())){var methodparams = method.GetParameters().Select(p => GetService(p.ParameterType)).ToArray();method.Invoke(instance, methodparams);}return instance;
}
三、测试验证
1、测试服务实例
public interface IFooService{void DoSomething();}public class FooService : IFooService
{private static int _instanceCount = 0;public int InstanceId { get; }public FooService(){InstanceId = ++_instanceCount;}public void DoSomething(){Console.WriteLine($"FooService instance {InstanceId} doing something...");}
}
2、需要依赖注入的实例
public class ConstructorInject_BarService
{private readonly IFooService _fooService; //构造函数注入public ConstructorInject_BarService(IFooService fooService){_fooService = fooService;} public void Execute(){_fooService.DoSomething();}
}
public class PropertyInject_BarService
{//属性注入[MyIocInject]public IFooService? FooService { get; set; } public void Execute(){FooService?.DoSomething();}
}
public class MethodInject_BarService
{private IFooService? _fooService;//方法注入[MyIocInject]public void Initialize(IFooService fooService){_fooService = fooService;}public void Execute(){_fooService?.DoSomething();}
}
3、使用示例代码
class Program
{static void Main(string[] args){var services = new ServiceCollection();services.AddSingleton<IFooService, FooService>();services.AddTransient<BarService, BarService>();services.AddScoped<IFooService, FooService>();var serviceProvider = services.BuildServiceProvider();Console.WriteLine("Resolving BarService for the first time:");using (var scope1 = serviceProvider.CreateScope()){var barService1 = scope1.GetService<BarService>();barService1.Execute();var scopedFooService1 = scope1.GetService<IFooService>();scopedFooService1.DoSomething();var scopedFooService2 = scope1.GetService<IFooService>();scopedFooService2.DoSomething();Console.WriteLine($"Scoped instances are same: {object.ReferenceEquals(scopedFooService1, scopedFooService2)}");}Console.WriteLine("\nResolving BarService for the second time:");using (var scope2 = serviceProvider.CreateScope()){var barService2 = scope2.GetService<BarService>();barService2.Execute();var scopedFooService3 = scope2.GetService<IFooService>();scopedFooService3.DoSomething();var scopedFooService4 = scope2.GetService<IFooService>();scopedFooService4.DoSomething();Console.WriteLine($"Scoped instances are same: {object.ReferenceEquals(scopedFooService3, scopedFooService4)}");}Console.WriteLine("\nResolving IFooService directly:");var fooService1 = serviceProvider.GetService<IFooService>();fooService1.DoSomething();var fooService2 = serviceProvider.GetService<IFooService>();fooService2.DoSomething();Console.WriteLine($"Singleton instances are same: {object.ReferenceEquals(fooService1, fooService2)}");}
}
4、运行结果
运行上述代码,您应该会看到类似以下的输出:
Resolving BarService for the first time:
FooService instance 1 doing something...
FooService instance 1 doing something...
FooService instance 2 doing something...
FooService instance 2 doing something...
Scoped instances are same: TrueResolving BarService for the second time:
FooService instance 3 doing something...
FooService instance 3 doing something...
FooService instance 4 doing something...
FooService instance 4 doing something...
Scoped instances are same: TrueResolving IFooService directly:
FooService instance 1 doing something...
FooService instance 1 doing something...
Singleton instances are same: True
从输出中可以看出:
- 在同一个作用域内,两次解析得到的
IFooService
实例是相同的。 - 在不同的作用域内,两次解析得到的
IFooService
实例是不同的。 - 单例(Singleton)实例在整个应用程序生命周期内只有一个实例。
四、总结
这个一个手写IOC容器的简单示例,可以清晰地了解IOC容器地实现原理及作用,希望该示例能帮助大家理解IOC容器地原理以及去手写实现逻辑,验证单例、瞬时和作用域实例的生命周期等。