My.Ioc 代碼示例——Lifetime 和 ILifetimeScope

不少 Ioc 框架在建立對象的過程當中,都會採起某種方式來緩存/複用/釋放已構建的對象。在 My.Ioc 中,這個目的是經過 Lifetime/ILifetimeScope 來實現的。其中,Lifetime 實現了緩存/複用對象的功能,ILifetimeScope 則實現了複用/釋放對象的功能。緩存

My.Ioc 默認提供了三種 Lifetime:ContainerLifetime、TransientLifetime 和 ScopeLifetime。這裏簡單解釋一下它們的含義:ContainerLifetime 繼承自 SingletonLifetime,它其實是一種單例模式的實現。TransientLifetime 顧名思義,即每次請求都新建一個對象返回給調用者。ScopeLifetime 則代表在某個 scope 及其父 scope 中建立的對象將在該 scope 內複用。框架

上面這樣解釋也許你們不是很容易明白,下面咱們結合示例代碼來講明:ide

using System;
using System.Diagnostics;
using My.Ioc;
using My.Ioc.Exceptions;

namespace LifetimeAndLifetimeScope
{
    #region Test Types

    public class SingletonDisposableClass : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine("Disposing SingletonDisposableClass...");
        }
    }
    public class SingletonNonDisposableClass { }

    public class TransientNonDisposableClass { }
    public class TransientDisposableClass : IDisposable 
    { 
        public void Dispose()
        {
            Console.WriteLine("Disposing TransientDisposableClass...");
        }
    }

    public class ScopedDisposableClass : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine("Disposing ScopedDisposableClass...");
        }
    }
    public class ScopedNonDisposableClass
    {
    }

    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            var container = new ObjectContainer(false);
            Register(container);
            
            ResolveTransient(container);
            ResolveScope(container);
            ResolveSingleton(container);
            
            // Dispose the container
            // The disposable singleton instances should be disposed here.
            container.Dispose();
            Console.ReadLine();
        }

        static void Register(IObjectContainer container)
        {
            container.Register<TransientDisposableClass>()
                .In(Lifetime.Transient()); // This line can be omitted
            container.Register<TransientNonDisposableClass>()
                .In(Lifetime.Transient()); // This line can be omitted

            container.Register<ScopedDisposableClass>()
                .In(Lifetime.Scope());
            container.Register<ScopedNonDisposableClass>()
                .In(Lifetime.Scope());
            
            container.Register<SingletonDisposableClass>()
                .In(Lifetime.Container()); //Singleton
            container.Register<SingletonNonDisposableClass>()
                .In(Lifetime.Container()); //Singleton
            
            container.CommitRegistrations();
        }

        static void ResolveTransient(IObjectContainer container)
        {
            var nonDisposable1 = container.Resolve<TransientNonDisposableClass>();
            var nonDisposable2 = container.Resolve<TransientNonDisposableClass>();
            Debug.Assert(nonDisposable1 != nonDisposable2, "nonDisposable1 == nonDisposable2");

            try
            {
                var disposable_Error = container.Resolve<TransientDisposableClass>();
            }
            catch (Exception ex)
            {
                Debug.Assert(ex is InvalidLifetimeScopeException);
            }

            using (var scope = container.BeginLifetimeScope())
            {
                var disposable1 = container.Resolve<TransientDisposableClass>();
                var disposable2 = container.Resolve<TransientDisposableClass>();
                Debug.Assert(disposable1 != disposable2, "disposable1 == disposable2");
            }
        }

        static void ResolveScope(IObjectContainer container)
        {
            try
            {
                var nondisposable_Error = container.Resolve<ScopedNonDisposableClass>();
            }
            catch (Exception ex)
            {
                Debug.Assert(ex is InvalidLifetimeScopeException);
            }

            string nested_scope_should_share_instance =
                "{0} should be the same to {1}, because the {0} is resolved in the outer scope, "
                + "which is shared with the scope where {1} is resolved, so they must be a same instance.";

            string same_scope_should_share_instance =
                "{0} should be the same to {1}, because they are resolved in the same scope.";
           
            using (var scope1 = container.BeginLifetimeScope())
            {
                var disposable1 = scope1.Resolve<ScopedDisposableClass>();
                var nonDisposable1 = scope1.Resolve<ScopedNonDisposableClass>();
                
                using (var scope2 = scope1.BeginLifetimeScope())
                {
                    var disposable2 = scope2.Resolve<ScopedDisposableClass>();
                    var disposable3 = scope2.Resolve<ScopedDisposableClass>();
                    Debug.Assert(disposable1 == disposable2,
                        string.Format(nested_scope_should_share_instance, "disposable1", "disposable2"));
                    Debug.Assert(disposable1 == disposable3,
                        string.Format(nested_scope_should_share_instance, "disposable1", "disposable3"));
                    Debug.Assert(disposable2 == disposable3,
                        string.Format(same_scope_should_share_instance, "disposable2", "disposable3"));
                    
                    var nonDisposable2 = scope1.Resolve<ScopedNonDisposableClass>();
                    var nonDisposable3 = scope1.Resolve<ScopedNonDisposableClass>();
                    Debug.Assert(nonDisposable1 == nonDisposable2,
                        string.Format(nested_scope_should_share_instance, "nonDisposable1", "nonDisposable2"));
                    Debug.Assert(nonDisposable1 == nonDisposable3,
                        string.Format(nested_scope_should_share_instance, "nonDisposable1", "nonDisposable3"));
                    Debug.Assert(nonDisposable2 == nonDisposable3,
                        string.Format(same_scope_should_share_instance, "nonDisposable2", "nonDisposable3"));
                }
            }
        }
        
        static void ResolveSingleton(IObjectContainer container)
        {
            var disposable1 = container.Resolve<SingletonDisposableClass>();
            var disposable2 = container.Resolve<SingletonDisposableClass>();
            Debug.Assert(disposable1 == disposable2, "disposable1 != disposable2");
            
            var nonDisposable1 = container.Resolve<SingletonDisposableClass>();
            var nonDisposable2 = container.Resolve<SingletonDisposableClass>();
            Debug.Assert(nonDisposable1 == nonDisposable2, "disposable1 != nonDisposable2");
        }
    }
}
View Code

在示例中,咱們設計了這麼三對類型:SingletonDisposableClass/SingletonNonDisposableClass、ScopedDisposableClass/ScopedNonDisposableClass 以及 TransientNonDisposableClass/TransientDisposableClass。用意很簡單,分別註冊到上面三種類型的 Lifetime 中。而每一類對象之因此有 Disposable 和 NonDisposable 兩個類型,是由於咱們要展現這三類 Lifetime 中對象清理的策略。spa

咱們首先在 Register 方法中將 SingletonDisposableClass/SingletonNonDisposableClass 註冊爲 Container 生命週期,將 ScopedDisposableClass/ScopedNonDisposableClass 註冊爲 Scope 生命週期,並將 TransientNonDisposableClass/TransientDisposableClass 註冊爲 Transient 生命週期。設計

作好了準備工做以後,下面咱們要讓容器來爲咱們建立上述對象。首先,咱們看 ResolveTransient 這個方法。這個方法旨在告訴咱們:code

  1. 若是對象被註冊爲 Transient 生命週期,那麼當咱們請求容器解析對象時,不管咱們是否使用 ILifetimeScope,每次返回給咱們的都是容器新建立的對象。也就是說,兩次解析返回的結果不是同一個對象
  2. 若是對象實現了 IDisposable 接口,那麼在解析該對象時,就必須先向容器請求一個 ILifetimeScope,而後在該 ILifetimeScope 中解析對象。不然,您將會收到一個 InvalidLifetimeScopeException 的異常。並且,在 ILifetimeScope 結束 (Dispose) 時,所請求的對象也會隨之被清理 (Dispose)

接着,咱們來看一下 ResolveScope 這個方法。這個方法是咱們用來演示 ScopeLifetime 的運行方式的。它告訴咱們:orm

  1. 當對象被註冊爲 Scope 生命週期時,不管其是否實現 IDisposable 接口,在解析時咱們都必須先向容器請求一個 ILifetimeScope,而後在該 ILifetimeScope 中解析對象。不然,您將會收到一個 InvalidLifetimeScopeException 的異常。
  2. 只有實現了 IDisposable 接口的對象纔會在解析後,隨着自身所處的 ILifetimeScope 的結束而被清理 (Dispose)。
  3. ILifetimeScope 是能夠嵌套的。例如,在示例代碼中,scope2 是嵌套在 scope1 中的。在這種狀況下,咱們將 scope1 稱爲外層(父) scope,將 scope2 稱爲內層(子) scope。
  4. 內層 scope 能夠共享外層 scope 的對象,因此咱們纔會看到示例代碼中有 [disposable1 == disposable2]、[nonDisposable1 == nonDisposable2] 等那麼多相等性斷言。

最後咱們來看 ResolveSingleton 這個方法。咱們使用這個方法來觀察 ContainerLifetime 的運行方式。這個方法的運行結果告訴咱們:對象

  1. 若是對象被註冊爲 Container 生命週期,那麼當咱們請求容器解析對象時,不管該對象是否實現了 IDisposable 接口,都不須要使用事先向容器請求 ILifetimeScope。但固然,要使用 ILifetimeScope 來解析它們也是能夠的,最後結果應該都是返回相同實例。
  2. 若是所解析的對象實現了 IDisposable 接口,那麼它要一直等到容器 (IObjectContainer) 自己的生命週期結束以後才被清理。

 

本文源碼可在此處下載,壓縮包中包含了 My.Ioc 框架的源碼和本示例以及其餘一些示例的源碼。blog

相關文章
相關標籤/搜索