zoukankan      html  css  js  c++  java
  • My.Ioc 代码示例——使用条件绑定和元数据(可选)构建插件树

    本文旨在通过创建一棵插件树来演示条件绑定和元数据的用法。

    说“插件树”也许不大妥当,因为在一般观念中,谈到插件树,我们很容易会想到 Winform/Wpf 中的菜单。举例来说,如果要在 Winform 中创建一个层级菜单,我们可以使用类似如下代码:

    // Create File menu
    var newMenu = new ToolStripMenuItem();
    
    var localProjectMenu = new ToolStripMenuItem();
    var remoteProjectMenu = new ToolStripMenuItem();
    var openMenu = new ToolStripMenuItem();
    openMenu.DropDownItems.AddRange(new ToolStripItem[] { localProjectMenu, remoteProjectMenu});
    
    var fileMenu = new ToolStripMenuItem();
    fileMenu.DropDownItems.AddRange(new ToolStripItem[] { openMenu, newMenu });
    fileMenu.Size = new System.Drawing.Size(39, 21);
    fileMenu.Text = "&File";
    
    // Create Edit menu
    var undoMenu = new ToolStripMenuItem();
    var redoMenu = new ToolStripMenuItem();
    
    var editMenu = new ToolStripMenuItem();
    editMenu.DropDownItems.AddRange(new ToolStripItem[] { undoMenu, redoMenu});
    
    // Create MenuStrip
    var menuStrip = new MenuStrip();
    menuStrip.Items.AddRange(new ToolStripItem[] { fileMenu, editMenu});

    这样创建出来,就是一个类似如下结构的菜单树:

     

    在上述示例中,我们通过分别创建各个菜单对象(并为各个菜单指定不同的属性,例如 Text/Size),然后将这些菜单对象插入对应的上级菜单或 MenuStrip 中,最后组装成一棵菜单树。这里,菜单类型(ToolStripMenuItem/ToolStripItem 等类型)并没有增加,增加的只是菜单类型的实例数量。换句话说,此处我们是通过增加对象实例而不是对象类型的数量来实现复用。

    在 My.Ioc 中,通过为相同契约 (Contract Type) 提供多个不同实现,并将这些实现分别注册到容器中,同时结合 My.Ioc 的条件绑定和元数据功能,我们也可以方便地构建起一棵树。请看下面的示例代码:

    using System;
    using System.Collections.Generic;
    using My.Ioc;
    
    namespace TreeBuilder
    {
        #region Tree/Node Types
    
        public interface INode
        {
            string ParentName { get; set; }
            string Name { get; }
            IEnumerable<INode> ChildNodes { get; }
        }
    
        public abstract class Node : INode
        {
            string _name;
    
            public string ParentName { get; set; }
            public string Name
            {
                get
                {
                    _name = _name ?? GetType().Name;
                    return _name;
                }
            }
    
            public virtual IEnumerable<INode> ChildNodes
            {
                get { return null; }
            }
        }
    
        #region Level 1
    
        public class Tree
        {
            readonly string _name;
            readonly IEnumerable<INode> _childNodes;
    
            public Tree(IEnumerable<INode> childNodes)
            {
                if (childNodes == null)
                    throw new ArgumentException();
                _name = GetType().Name;
                foreach (var childNode in childNodes)
                    childNode.ParentName = _name;
                _childNodes = childNodes;
            }
    
            public string Name
            {
                get { return _name; }
            }
    
            public IEnumerable<INode> ChildNodes
            {
                get { return _childNodes; }
            }
        }
    
        #endregion
    
        #region Level 2
    
        public abstract class CompositeNode : Node
        {
            readonly IEnumerable<INode> _childNodes;
    
            protected CompositeNode(IEnumerable<INode> childNodes)
            {
                if (childNodes == null)
                    throw new ArgumentException();
                foreach (var childNode in childNodes)
                    childNode.ParentName = Name;
                _childNodes = childNodes;
            }
    
            public override IEnumerable<INode> ChildNodes
            {
                get { return _childNodes; }
            }
        }
    
        public class ListNode : CompositeNode
        {
            public ListNode(List<INode> childNodes)
                : base(childNodes)
            {
            }
        }
    
        public class ArrayNode : CompositeNode
        {
            public ArrayNode(INode[] childNodes)
                : base(childNodes)
            {
            }
        }
    
        #endregion
    
        #region Level 3
    
        public class ChildNode1 : Node
        {
        }
    
        public class ChildNode2 : Node
        {
        }
    
        public class ChildNode3 : Node
        {
        }
    
        public class ChildNode4 : Node
        {
        }
    
        public class ChildNode5 : Node
        {
        }
    
        public class ChildNode6 : Node
        {
        }
    
        public class ChildNode7 : Node
        {
        }
    
        public class ChildNode8 : Node
        {
        }
    
        #endregion
    
        #endregion
    
        class Program
        {
            static void Main(string[] args)
            {
                IObjectContainer container = new ObjectContainer(false);
                Register(container);
    
                // Try to get an observer
                IObjectObserver<Tree> treeObserver;
                if (!container.TryGetObserver(out treeObserver))
                    throw new InvalidOperationException();
    
                // Resolve the tree using the observer
                var tree = container.Resolve(treeObserver);
                //var tree = container.Resolve<Tree>();
                PrintTreeMembers(tree, 1);
    
                // Add more nodes at runtime
                container.Register(typeof(INode), typeof(ChildNode8))
                    .WhenParentMetadata((mata) => string.Equals("ArrayNode", mata));
                container.Register<INode, ChildNode7>()
                    .WhenParentTypeIs<ListNode>();
    
                // Commit the registrations to the registry as usual.
                container.CommitRegistrations();
    
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
    
                // Resolve the tree again
                tree = container.Resolve(treeObserver);
                //tree = container.Resolve<Tree>();
                PrintTreeMembers(tree, 2);
    
                Console.ReadLine();
            }
    
            static void Register(IObjectContainer container)
            {
                container.Register<Tree>();
    
                container.Register<INode, ArrayNode>()
                    .WhenParentTypeIs<Tree>()
                    .Set("ArrayNode");
                container.Register<INode, ListNode>()
                    .WhenParentTypeIs<Tree>()
                    .Set("ListNode");
    
                #region Inject into ArrayNode
    
                container.Register(typeof(INode), typeof(ChildNode1))
                    .WhenParentMetadata((mata) => string.Equals("ArrayNode", mata));
    
                container.Register<INode, ChildNode2>()
                    .WhenParentTypeIs<ArrayNode>();
    
                container.Register<INode, ChildNode3>()
                    .WhenParentTypeIs<ArrayNode>();
    
                container.Register<INode, ChildNode4>()
                    .WhenParentTypeIs<ArrayNode>();
    
                #endregion
    
                #region Inject into ListNode
    
                container.Register<INode, ChildNode5>()
                    .WhenParentTypeIs<ListNode>();
    
                container.Register<INode, ChildNode6>()
                    .WhenParentTypeIs<ListNode>();
    
                #endregion
    
                // Commit the registrations to the registry.
                container.CommitRegistrations();
            }
    
            static void PrintTreeMembers(Tree tree, int time)
            {
                if (tree == null)
                    throw new ArgumentException();
    
                Console.WriteLine(time);
                Console.WriteLine("=================================================");
    
                Console.WriteLine(tree.Name);
    
                // Breadth first traversal
                var allNodes = new List<INode>();
                if (tree.ChildNodes == null)
                    return;
                allNodes.AddRange(tree.ChildNodes);
                for (int i = 0; i < allNodes.Count; i++)
                {
                    var node = allNodes[i];
                    Console.WriteLine(node.ParentName + "/" + node.Name);
                    if (node.ChildNodes != null)
                        allNodes.AddRange(node.ChildNodes);
                }
            }
        }
    }
    View Code

    请注意,与前一种方式相比,以这种方式来构建树的思想实际上是有所不同的。从本质上来说,这是一种通过增加(派生)类型来实现复用的手段,而前一种方式是通过增加对象实例来实现复用,这一点我们前面说过了。与前一种复用相比,这种复用有一个缺点,那就是当我们将这些类型注册到容器中时,My.Ioc 会为每个派生类型生成一个注册项,而每生成一个注册项时都会附带生成并缓存一大堆中间类(比如 ObjectBuilder/Lifetime/Injector/DependencyProvider 等),这无疑会多耗用一些内存。而前一种复用方式则无此弊病,因为它只是简单增加对象实例而已,这也是我在本文开头时说“不大妥当”的原因。

    尽管如此,恰如我们在文章开头提到的,我们的目的是阐述条件绑定和元数据的用法,通过这个示例,我们达到了这个目的。至于如何更好地使用条件绑定和元数据的功能,留给各位自己去发挥好了。

    源码可在此处下载,压缩包中包含了 My.Ioc 框架的源码和本示例以及其他一些示例的源码。

  • 相关阅读:
    [ZJOI2006]书架
    [NOI2005]维护数列
    Python 最佳实践
    python中使用多继承
    python 抽象类、抽象方法的实现
    30个有关Python的小技巧
    一行 Python 实现并行化 -- 日常多线程操作的新思路
    python日志模块logging
    在Python中怎么表达True
    有趣的库:pipe(类似linux | 管道)库
  • 原文地址:https://www.cnblogs.com/johnny-liu/p/3962332.html
Copyright © 2011-2022 走看看