您的位置:首页 > 编程语言

My.Ioc 代码示例——使用条件绑定和元数据(可选)构建插件树

2014-09-09 11:15 274 查看
本文旨在通过创建一棵插件树来演示条件绑定和元数据的用法。

说“插件树”也许不大妥当,因为在一般观念中,谈到插件树,我们很容易会想到 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});


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

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 框架的源码和本示例以及其他一些示例的源码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: