1. 程式人生 > >手把手教你寫DI_2_小白徒手擼建構函式注入

手把手教你寫DI_2_小白徒手擼建構函式注入

小白徒手擼建構函式注入

在上一節:手把手教你寫DI_1_DI框架有什麼?

我們已經知道我們要擼哪些東西了

那麼我們開始動工吧,這裡呢,我們找小白同學來表演下

小白同學 :我們先定義一下我們的廣告招聘紙有什麼:

public abstract class ServiceDefintion  // 小白同學 :我們換個名字,不叫 ServiceDescriptor ,擼也要擼出自己的標誌嘛
{
    public abstract Type ServiceType { get; }
    public abstract Type ImplementationType { get; }
    public abstract Lifetime Lifetime { get; }
}

public enum Lifetime  // 小白同學 :定義一下要支援的三種生命週期
{
    Singleton,
    Scoped,
    Transient
}

// 小白同學 :搞個集合存放他們
public interface IServiceDefintions : IEnumerable<ServiceDefintion>
{
    void Add(ServiceDefintion serviceDefintion);
}

public class ServiceDefintions : IServiceDefintions
{
    private readonly List<ServiceDefintion> services = new List<ServiceDefintion>();

    public void Add(ServiceDefintion serviceDefintion)
    {
        if (serviceDefintion == null)
        {
            throw new ArgumentNullException(nameof(serviceDefintion));
        }
        services.Add(serviceDefintion);
    }
}

好,我們實現兩種不同的廣告型別

public class TypeServiceDefintion : ServiceDefintion  // 小白同學 :這種是隻提供型別的,怎麼建立例項需要我們解析生成,但是對使用DI的人來說,很方便,不用管怎麼去new
{
    public override Type ServiceType { get; }
    public override Type ImplementationType { get; }
    public override Lifetime Lifetime { get; }

    public TypeServiceDefintion(Type serviceType, Type implementationType, Lifetime lifetime)
    {
        ServiceType = serviceType;
        ImplementationType = implementationType;
        Lifetime = lifetime;
    }
}

public interface IImplementationFactory
{
    Func<INamedServiceProvider, object> ImplementationFactory { get; }
}

public class DelegateServiceDefintion : ServiceDefintion, IImplementationFactory  // 小白同學 :這種是使用者自己new物件,少數特殊情況,使用者會自己寫特殊邏輯,所以我們需要提供支援
{
    public DelegateServiceDefintion(Type serviceType, Type implementationType, Lifetime lifetime,
        Func<IServiceProvider, object> implementationFactory)
    {
        ServiceType = serviceType;
        ImplementationType = implementationType;
        Lifetime = lifetime;
        ImplementationFactory = implementationFactory;
    }

    public override Type ServiceType { get; }

    public override Type ImplementationType { get; }

    public override Lifetime Lifetime { get; }

    public Func<IServiceProvider, object> ImplementationFactory { get; }
}

小白同學 :好了,我們有服務定義描述了,來建立IServiceProvider

public class ServiceProvider : IServiceProvider
{
    private readonly IServiceDefintions services;

    public ServiceProvider(IServiceDefintions services)
    {
        this.services = services;
    }
    public object GetService(Type serviceType)
    {
        var defintion = TryGetDefintion(serviceType); // 小白同學 :查詢一下服務定義
        if (defintion != null)
        {
            switch (defintion.Lifetime)
            {
                case Lifetime.Singleton:
                    return null;  // 小白同學 :啥?怎麼處理?emm 後面說吧,腦容量不夠啦

                case Lifetime.Scoped:
                    return null;  // 小白同學 :啥?怎麼處理?emm 後面說吧,腦容量不夠啦

                case Lifetime.Transient:
                    if(defintion is DelegateServiceDefintion defi)
                    {
                        return defi.ImplementationFactory(this);
                        // 小白同學 :haha, 不用我們做,真簡單
                    }
                    else // 小白同學 :TypeServiceDefintion
                    {
                        // 小白同學 :啥?怎麼處理?emm 後面說吧,腦容量不夠啦
                    }

                default:
                    return null;
            }
        }
        else
        {
            return null;
        }
    }

    private ServiceDefintion TryGetDefintion(Type serviceType)
    {
        return services.FirstOrDefault(i => i.ServiceType == serviceType);  //大神: what ? 每次都遍歷一下?太low了吧?
    }

}

小白同學 :捂臉.gif 我們居然每次都遍歷,簡直太笨了,趕緊改下,免得大神吐槽

public class ServiceProvider : IServiceProvider
{
    private readonly Dictionary<Type, ServiceDefintion> services;

    public ServiceProvider(IServiceDefintions services)
    {
        this.services = services.ToDictionary(i => i.ServiceType);  
        //大神: what 1 ? 有相同的 ServiceType 怎麼辦? 
    }

    private ServiceDefintion TryGetDefintion(Type serviceType)  //大神: what 2 ? 這個方法怎麼這麼怪
    {
        services.TryGetValue(serviceType, out var defintion);  
        return defintion;
    }

    ...
}

小白同學 :又被吐槽了,再改下

public class ServiceProvider : IServiceProvider
{
    private readonly Dictionary<Type, ServiceDefintion[]> services;  
    //大神: 呵呵,你就這樣寫吧,我打賭100塊你後面肯定要改
    //小白同學: ......

    public ServiceProvider(IServiceDefintions services)
    {
        this.services = services.GroupBy(i => i.ServiceType).ToDictionary(i => i.Key, i => i.ToArray());
    }

    private ServiceDefintion TryGetDefintion(Type serviceType) 
    {
        return services.TryGetValue(serviceType, out var defintion) ? defintion.LastOrDefault() : null;
    }

    ...
}

小白同學: 好了,我們簡單測試一下

[Fact]
public void Test()
{
    var a = new ServiceDefintions();
    a.Add(new DelegateServiceDefintion(typeof(TransientTest),typeof(TransientTest),Lifetime.Transient,i => this));
    var service = new ServiceProvider(a);

    var result0 = service.GetService(typeof(TransientTest));
    Assert.Same(this, result0);
}
// 大神: 你用this 去測瞬態?你確定this是瞬態的func 每次都會呼叫?
// 小白同學: 我。。。。。。我改
[Fact]
public void Test()
{
    var a = new ServiceDefintions();
    a.Add(new DelegateServiceDefintion(typeof(TransientTest),typeof(TransientTest),Lifetime.Transient,i => new TransientTest()));
    var service = new ServiceProvider(a);

    var result0 = service.GetService(typeof(TransientTest));
    var result1 = service.GetService(typeof(TransientTest));
    Assert.NotNull(result0);
    Assert.NotNull(result1);
    Assert.NotSame(result0, result1);
}

小白同學: 我們來做TypeServiceDefintion 解析支援

public class ServiceProvider : IServiceProvider
{
    public object GetService(Type serviceType)
    {
        ...

        case Lifetime.Transient:
            if(defintion is DelegateServiceDefintion defi)
            {
                return defi.ImplementationFactory(this);
            }
            else
            {
                var d = defintion as TypeServiceDefintion;
                var constructor = d.ImplementationType.GetConstructors().FirstOrDefault(i => i.IsPublic); // 小白同學:  反射獲取建構函式
                var ps = constructor.GetParameters();
                var args = new object[ps.Length];
                for (int j = 0; j < ps.Length; j++)
                {
                    var p = ps[j];
                    args[j] = i.GetService(p.ParameterType);  // 小白同學:  獲取引數值
                }
                return constructor.Invoke(args);  // 小白同學:  建立;
            }

        ....
    }
}

小白同學: 你看我寫的不錯吧

大神:呵呵,這樣反射效能你考慮了嗎? 泛型你考慮了嗎? 還有你每次都重新生成DelegateServiceDefintion?

小白同學: 我知道反射該用IL或者表示式樹處理,但觀眾不好看嘛

大神:呵呵,你不會寫吧,你看看人家lemon大神怎麼寫的 - file

小白同學: 好,我下來學習。 泛型不過是再動態生成一下型別嘛,這樣就行啦

public class ServiceProvider : IServiceProvider
{
    public object GetService(Type serviceType)
    {
        ...

        case Lifetime.Transient:
            if(defintion is DelegateServiceDefintion defi)
            {
                return defi.ImplementationFactory(this);
            }
            else
            {
                var d = defintion as TypeServiceDefintion;
                var implementationType = serviceType.IsConstructedGenericType 
                    ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments)
                    : d.ImplementationType;
                var constructor = implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic); // 小白同學:  反射獲取建構函式
                .....
            }

        ....
    }
}

小白同學: 哦,還有快取:

public class ServiceProvider : IServiceProvider
{
    private readonly Dictionary<Type, ConstructorInfo> cache = new Dictionary<Type, ConstructorInfo>();

    public object GetService(Type serviceType)
    {
        ...

        case Lifetime.Transient:
            if(defintion is DelegateServiceDefintion defi)
            {
                return defi.ImplementationFactory(this);
            }
            else
            {
                ConstructorInfo constructor = null;
                if(cache.ContainsKey(serviceType))
                {
                    constructor = cache[serviceType];
                }
                else 
                {
                    var d = defintion as TypeServiceDefintion;
                    var implementationType = serviceType.IsConstructedGenericType 
                        ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments)
                        : d.ImplementationType;
                    constructor = cache[serviceType] = implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic); // 小白同學:  反射獲取建構函式
                }
                ....
            }

        ....
    }
}

大神: .......我想自閉..... 你都不考慮多執行緒嗎?

小白同學: !!! 我,我,我,我換成它 ConcurrentDictionary<Type, ConstructorInfo> cache

大神:算你NB,SingletonScoped 你打算怎麼做?

小白同學: 簡單, copy 一下

public class ServiceProvider : IServiceProvider
{
    private readonly ConcurrentDictionary<Type, ConstructorInfo> cache = new ConcurrentDictionary<Type, ConstructorInfo>();

    public object GetService(Type serviceType)
    {
        case Lifetime.Singleton:
                if(defintion is DelegateServiceDefintion defi)
                {
                    return defi.ImplementationFactory(this);
                }
                else
                {
                    ConstructorInfo constructor = cache.GetOrAdd(serviceType, i => 
                    {
                        var d = defintion as TypeServiceDefintion;
                        var implementationType = serviceType.IsConstructedGenericType 
                            ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments)
                            : d.ImplementationType;
                        return implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic);
                    });
                    ar ps = constructor.GetParameters();
                    var args = new object[ps.Length];
                    for (int j = 0; j < ps.Length; j++)
                    {
                        var p = ps[j];
                        args[j] = i.GetService(p.ParameterType);  // 小白同學:  獲取引數值
                    }
                    return constructor.Invoke(args);  // 小白同學:  建立;
                }

        case Lifetime.Scoped:
                if(defintion is DelegateServiceDefintion defi)
                {
                    return defi.ImplementationFactory(this);
                }
                else
                {
                    ConstructorInfo constructor = cache.GetOrAdd(serviceType, i => 
                    {
                        var d = defintion as TypeServiceDefintion;
                        var implementationType = serviceType.IsConstructedGenericType 
                            ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments)
                            : d.ImplementationType;
                        return implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic);
                    });
                    ar ps = constructor.GetParameters();
                    var args = new object[ps.Length];
                    for (int j = 0; j < ps.Length; j++)
                    {
                        var p = ps[j];
                        args[j] = i.GetService(p.ParameterType);  // 小白同學:  獲取引數值
                    }
                    return constructor.Invoke(args);  // 小白同學:  建立;
                }

        case Lifetime.Transient:
            if(defintion is DelegateServiceDefintion defi)
            {
                return defi.ImplementationFactory(this);
            }
            else
            {
                ConstructorInfo constructor = cache.GetOrAdd(serviceType, i => 
                {
                    var d = defintion as TypeServiceDefintion;
                    var implementationType = serviceType.IsConstructedGenericType 
                        ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments)
                        : d.ImplementationType;
                    return implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic);
                });
                ar ps = constructor.GetParameters();
                var args = new object[ps.Length];
                for (int j = 0; j < ps.Length; j++)
                {
                    var p = ps[j];
                    args[j] = i.GetService(p.ParameterType);  // 小白同學:  獲取引數值
                }
                return constructor.Invoke(args);  // 小白同學:  建立;
            }

        ....
    }
}

大神:我!!!!!!!!!! 我給你一刀!!!!!!!

小白同學: 啊!!!!!!!!!

由於小白同學受傷,本次節目中斷,等小白同學養好傷,我們再繼續

下一章 小白徒手支援 SingletonScoped 生命週期