简单实现的一个依赖注入框架 (.Net Core7)

发布时间 2023-06-25 12:20:41作者: 灵火

根据 Microsoft.Extensions.DepdencyInjection 框架简单仿写的一个 DI 框架。
官方地址:https://github.com/dotnet/runtime/tree/main/src/libraries/Microsoft.Extensions.DependencyInjection

测试示例;

using MyDI.Core;

ServiceCollection sc = new ServiceCollection();
sc.AddScoped<IPerson, Student>();
sc.AddSingleton<Student>(new Student("A"));
var provider = sc.Build();

var student = provider.GetService<IPerson>();
student.Say();

var studentA = provider.GetService<Student>();
studentA.Say();

/*
 * output :
 *  Ani is a student.
 *  A is a student.
 */


#region Test Cases

public interface IPerson
{
    public void Say();
}

class Student : IPerson
{
    public Student()
    {
    }

    public Student(string name)
    {
        Name = name;
    }

    public string Name { get; } = "Ani";

    public void Say()
    {
        Console.WriteLine($"{Name} is a student.");
    }
}

#endregion

框架核心代码:

using System.Collections.ObjectModel;

namespace MyDI.Core;

#region Core

public interface IServiceProviderBuilder
{
    public IServiceProvider Build();
}

public class ServiceCollection : Collection<ServiceDescription>, IServiceProviderBuilder
{
    /// <inheritdoc />
    public IServiceProvider Build()
    {
        return new ServiceProvider(this);
    }
}

public static class ServiceCollectionExtensions
{
    public static void AddScoped<T, I>(this ServiceCollection serviceCollection) => serviceCollection.Add(new ServiceDescription(typeof(T), typeof(I)));

    public static void AddSingleton<T>(this ServiceCollection serviceCollection, object? value) => serviceCollection.Add(new ServiceDescription(typeof(T), value?.GetType(), value));
}

public class ServiceProvider : IServiceProvider
{
    private ServiceCollection _serviceCollection;

    public ServiceProvider(ServiceCollection serviceCollection)
    {
        _serviceCollection = serviceCollection;
    }

    /// <inheritdoc />
    public object? GetService(Type serviceType)
    {
        var description = _serviceCollection.FirstOrDefault(t => t.ServiceType == serviceType);
        switch (description)
        {
            case null:
                throw new InvalidOperationException("please register the service type.");
            case { Value: not null }:
                return description.Value;
        }

        var implementType = description.ImplementType;
        var constructorInfos = implementType?.GetConstructors();
        var len = constructorInfos?.Length;
        if (constructorInfos == null || len == 0)
        {
            throw new InvalidOperationException("please provide at least one public constructor.");
        }
        else if (len == 1)
        {
            var constructorInfo = constructorInfos[0];
            var result = constructorInfo.Invoke(null);
            description.Value = result;
            return result;
        }

        var paraments = description.Paraments;
        var paramentsLength = paraments?.Length;
        var constructorInfoT = constructorInfos.FirstOrDefault(t => t.GetParameters().Length == paramentsLength);
        if (constructorInfoT == null)
        {
            constructorInfoT = constructorInfos.FirstOrDefault(t => t.GetParameters().Length == 0);
        }

        if (constructorInfoT == null)
            throw new InvalidCastException("not matched for best constructor.");

        var value = constructorInfoT?.Invoke(paraments);
        description.Value = value;
        return value;
    }
}

public static class ServiceProviderExtensions
{
    public static T GetService<T>(this IServiceProvider provider)
    {
        var result = (T)provider.GetService(typeof(T))!;
        return result;
    }
}

public class ServiceDescription
{
    public ServiceDescription()
    {
    }

    public ServiceDescription(Type serviceType, Type? implementType)
    {
        ServiceType = serviceType;
        ImplementType = implementType;
    }

    public ServiceDescription(Type serviceType, Type? implementType, object? value)
    {
        ServiceType = serviceType;
        ImplementType = implementType;
        Value = value;
    }

    public ServiceDescription(Type serviceType, Type? implementType, object[] paraments, object? value)
    {
        ServiceType = serviceType;
        ImplementType = implementType;
        Paraments = paraments;
        Value = value;
    }

    public Type ServiceType { get; set; }
    public Type? ImplementType { get; set; }
    public object[] Paraments { get; set; } = Array.Empty<object>();
    public object? Value { get; set; }
    public ServiceScope ServiceScope { get; set; }
}

public class ServiceScope
{
}

#endregion