• MEF 编程指南(九):部件生命周期


    理解 MEF 容器部件生命周期和实现是非常重要的事情。考虑到 MEF 关注可扩展应用程序。这变得尤为重要。生命期可以解释为期望部件的共享性(transitively, its exports)

     
    共享,非共享与所有权(Share,Non Shared and ownership)
     
    部件的共享性(Shareability)是通过使用 PartCreationPolicyAttribute 定义的。PartCreationPolicyAttribute 提供以下几种值:
     
    • Shared:部件所有者告知 MEF 一个或多个部件的实例存在于容器。
    • NonShared: 部件所有者告知 MEF 每次对于部件导出的请求将会被一个新的实例处理。
    • Any 或者不支持的值: 部件所有者允许部件用作“Share”或者“NonShared”。
     
    可以使用 [System.ComponentModel.Composition.PartCreationPolicyAttribute] 定义创建策略:
     
        [PartCreationPolicy(CreationPolicy.NonShared)]
        [Export(typeof(IMessageSender))]
        public class SmtpSender : IMessageSender
        {
            public void Send(string message)
            {
                throw new NotImplementedException();
            }
        }
     
        public interface IMessageSender
        {
            void Send(string message);
        }
    容器总会有他所创建部件的所有权。换言之,所有权绝不会转移给使用容器实例(直接)或者通过导入(间接)请求者。
    导入也可以定义或者约束部件的创建策略,用于提供导入值。你说要做的是为 RequiredCreationPolicy 指定 CreationPolicy 枚举值:
     
        [Export]
        public class Importer
        {
            [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
            public Dependency Dep { get; set; }
        }
    部件可用性关联到导入者是非常有用的。默认情况下,RequiredCreationPolicy 被设置成 Any,所以 Shared 和 NonShared 部件都可以提供值。

    -Part.AnyPart.SharedPart.NonShared
    Import.Any Shared Shared Non Shared
    Import.Shared Shared Shared No Match
    Import.NonShared Non Shared No Match Non Shared
     
    注意:当双方都定义为“Any”的时候,结果会是 Shared 部件
     
    释放容器(Disposing the container)
     
    容器实例通常是容器持有部件的生命周期。部件实例由容器创建,生命周期受到容器生命周期的限制。结束容器生命周期的途径是调用 Disposing 方法。
    • 实现 IDisposable 的部件会调用 Dispose 方法
    • 容器中包含的部件引用将会被清除
    • 共享部件会被释放和清除
    • 容器释放后,延迟导出不会起作用
    • 该操作可能会抛出 System.ObjectDisposedException 异常
     
    容器和部件引用(Container and parts references)
     
    我们相信 .NET 垃圾回收器是做清理最适合的选择。然而,我们也需要提供一个拥有确定性行为的容器。因此,除非满足下面的条件容器,将不会保留它所创建的引用:
     
    • 部件被标记为 Shared
    • 部件实现了 IDisposable 接口
    • 一个或多个导入配置为允许重组
     
    对于上述情况,部件引用是保留的。结合实际目标,从容器中请求那些非共享部件,内存需求会很快成为一个问题。为了缓解这个问题,应该依靠接下来两节讨论的策略。
     
    域操作和早期资源回收(Scoped operations and early reclaim of resources)
     
    一些常见应用程序,比如:Web 应用程序(web apps) 和 Windows 服务(windows services)与桌面应用程序(Desk Applications)有很大的区别。他们更多的依靠批处理和短暂操作。

    对于那些场景,你应该要么使用子容器(下一节介绍)要么提前释放对象图。后者允许容器释放和清理对于对象图中 Shared 部件的引用 - 直到到达 Shared 部件。
     
    为了提前释放对象图,你需要调用 CompositionContrainer 公开的 ReleaseExport 方法: 
     
    var batchProcessorExport = container.GetExport<IBatchProcessor>();
     
    var batchProcessor = batchProcessorExport.Value;
    batchProcessor.Process();
     
    container.ReleaseExport(batchProcessorExport);
    下图描述一个对象图并显示哪些部件会被释放(引用移除,回收)哪些保持原状。
      
     


    作为 root 部件是 non shared,容器不会保留引用,所以大体上是无操作的。我们继续遍历图检查为 root 部件的导出。部件1既 non shared 又 disposable,所以部件被回收而且引用从容器移除。同样发生在部件2,然后。。。。。。。。
     
    注意那些深度优先方式实现的遍历图。
     
    容器层级(Container hierarchies)
     
    另一种方法处理同样的问题是使用层级容器。你可以创建容器并把他们连接到父容器,使之成为子容器。请注意,除非你为子容器提供不同的目录,这不会起到太大的作用,实例化同样会在父容器发生。
     
    因此,或者你应该指定一个全新的目录,公开一组应该由子容器创建的部件。我们期望子容器的生命期是短暂的,创建的部件会提前释放和回收。
     
    一种常见的方法是在父容器构建 Shared 部件以及在子容器上构建 Non Shared 部件。Shared 部件会依靠 Non Shared 部件导出,此外,主目录必须包括整组部件,反之,子容器应该仅仅包含主目录 non  shared 部件过滤的视图。

     
    获取该主题的更多信心,请参考:过滤目录
     
    回收序列

    回收序列总是不确定的。这意味你不应该尝试在你的 Dispose 方法上使用导入。例如:
     
    [Export]
    public class SomeService : IDisposable
    {
        [Import]
        public ILogger Logger { get; set; }
       
        public void Dispose()
        {
             Logger.Info("Disposing"); // might throw exception!
        }
    }
    在 dispose 方法实现中使用导入的 logger 实例可能会出错,作为 ILogger 约定的实现也可能会被回收掉,或者已经被回收了。
     
    AddPart/RemovePart
     
    不是每个部件都是由容器创建。也可以从容器添加和移除部件。这个过程触发组合并且可能开始为满足依赖递归添加部件的创建。  

    注意:MEF 永远不需要你提供实例的所有权,但是它确实有所创建的部件的所有权用以满足实例的导入。
     
    using System;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
     
    class Program
    {
        static void Main(string[] args)
        {
            var catalog = new AssemblyCatalog(typeof(Program).Assembly);
            var container = new CompositionContainer(catalog);
            var root = new Root();
     
            // add external part
            container.ComposeParts(root);
     
            // ... use the composed root instance
     
            // removes external part
            batch = new CompositionBatch();
            batch.RemovePart(root);
            container.Compose(batch);
        }
    }
     
    public class Root
    {
        [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
        public NonSharedDependency Dep { get; set; }
    }
     
    [Export, PartCreationPolicy(CreationPolicy.NonShared)]
    public class NonSharedDependency : IDisposable
    {
        public NonSharedDependency()
        {
        }
     
        public void Dispose()
        {
            Console.WriteLine("Disposed");
        }
    }
  • 相关阅读:
    canvas 学习
    configure/autoconf/automake文件相关
    git add A 和 git add . 的区别详解
    Vim格Vim格式化代码功能——gg=G式化代码功能——gg=G
    详解git commit amend 用法
    git reset HEAD 与 git reset hard HEAD的区别 天地逍遥
    git的撤销操作:reset、checkout和revert
    参与linux社区
    git制作补丁
    git diff、git diff head、git diff cached三者详细区分
  • 原文地址:https://www.cnblogs.com/JavCof/p/3689129.html
Copyright © 2020-2023  润新知