基于Visual Studio2010与C#4.0新功能和展望--dynamic:用DynamicObject创建包装器

在以前的帖子我展示了如何在运行时使用新的dynamic功能和ExpandoObject类来添加和删除属性,这可以使你的代码比使用LINQ代码写入XML语法更具可读性和灵活性。
但也有一些明显的缺陷在例子中:虽然ExpandoObject提供更好的语法,LINQ to XML的图书馆提供了有益的方法,帮助您使用XML文件很多。因此,有可能是这两个优势结合起来,有更好的语法,仍然可以得到所有这些方法?答案是肯定的,但是你需要另一种类型的System.Dynamic命名空间:DynamicObject。
该DynamicObject类使您可以获取或设置覆盖像一个成员操作,调用一个方法,或执行任何二进制,一元,或类型转换操作。为了说明问题,让我们创建一个非常简单的对象覆盖了“获取属性”操作,所以,每当你进入一个属性返回一个字符串属性的名称。这个例子没有任何实际价值。

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

正如ExpandoObject,我们必须使用动态关键字来创建这个类的实例。

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//打印"SampleProperty".
  

让我们看看会发生什么在这个例子上。当您调用obj.SampleProperty,动态语言运行时(DLR)使用的语言粘结剂寻找这一类属性在SampleObject静态定义。如果没有这样的属性,调用TryGetMember DLR的方法。这种方法得到的信息是什么财产要求通过粘结剂参数。正如你所看到的,binder.Name包含属性的实际名称。
该TryGetMember方法返回true,如果操作成功。但是,实际经营结果必须被分配到out参数的结果。在这个例子中,TryGetMember返回true,但obj.SampleProperty返回“SampleProperty”。
现在让我们转向更为复杂的例子,并创造一个XElement对象包装。再次,我会尽量提供下列LINQ到XML示例更好的语法。

XElement contactXML =
    new XElement("Contact",
    new XElement("Name", "Patrick Hines"),
    new XElement("Phone", "206-555-0144"),
    new XElement("Address",
        new XElement("Street1", "123 Main St"),
        new XElement("City", "Mercer Island"),
        new XElement("State", "WA"),
        new XElement("Postal", "68042")
    )
);

首先,我要创造一个ExpandoObject模拟。我仍然希望能够动态地添加和删除属性。但由于我基本上是创造一个有利于XElement类型的包装,我将使用XElement而不是字典保持性能。

public class DynamicXMLNode : DynamicObject
{
    XElement node;
    public DynamicXMLNode(XElement node)
    {
        this.node = node;
    }
    public DynamicXMLNode()
    {
    }
    public DynamicXMLNode(String name)
    {
        node = new XElement(name);
    }
    public override bool TrySetMember(
        SetMemberBinder binder, object value)
    {
        XElement setNode = node.Element(binder.Name);
        if (setNode != null)
            setNode.SetValue(value);
        else
        {
            if (value.GetType() == typeof(DynamicXMLNode))
                node.Add(new XElement(binder.Name));
            else
                node.Add(new XElement(binder.Name, value));
        }
        return true;
    }
    public override bool TryGetMember(        GetMemberBinder binder, out object result)
    {
        XElement getNode = node.Element(binder.Name);
        if (getNode != null)
        {
            result = new DynamicXMLNode(getNode);
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }
}

这里是你如何可以使用这个类。

dynamic contact = new DynamicXMLNode("Contacts");
contact.Name = "Patrick Hines";
contact.Phone = "206-555-0144";
contact.Address = new DynamicXMLNode();
contact.Address.Street = "123 Main St";
contact.Address.City = "Mercer Island";
contact.Address.State = "WA";
contact.Address.Postal = "68402";

让我们看看在接触对象。当这个对象被创建时,它会初始化内部的XElement。如果您设置一个属性值,在contact.Phone =“206-555-0144喜欢”,该TrySetMember方法检查是否具有名称电话元素在其XElement存在。如果它不存在,该方法创建的元素。
下一个有趣的路线是contact.Address =新DynamicXMLNode()。基本上,在这个特殊的例子,我此行就是要创建一个节点有子节点。对于此属性,TrySetMember方法创建一个XElement没有值。
最复杂的案件在这里是一个如contact.Address.State行=“娃”。首先,TryGetMember方法要求contact.Address并返回一个新的DynamicXMLNode对象,它是由具有名称地址XElement初始化。 (理论上,我可以回到自己的XElement,但是这将让这个例子更加复杂化。)然后TrySetMember方法被调用。该方法查找在contact.Address国的因素。如果它没有找到一个,它创建它。
因此,我已成功地创建了所需的XML结构。但TryGetMember总是返回一个DynamicXMLNode实例。我如何获取XML节点的实际价值?举例来说,我希望下面的行工作,但现在它抛出一个异常。
弦乐状态= contact.Address.State,我这里有几种选择。我可以修改TryGetMember方法返回实际值的叶节点,例如。但是,让我们探讨一个选项:覆盖类型转换。只需将以下方法添加到DynamicXMLNode类。

public override bool TryConvert(
    ConvertBinder binder, out object result)
{
    if (binder.Type == typeof(String))
    {
        result = node.Value;
        return true;
    }
    else
    {
        result = null;
        return false;
    }
}

现在,每当我有一个明确的DynamicXMLNode式或隐式类型转换,TryConvert方法被调用。该方法检查的对象是什么类型转换为,如果该类型是String,该方法返回的内部XElement价值。否则,它返回false,这意味着该语言应确定下一步该怎么做在大多数情况下(这意味着你将得到一个运行时异常)。
最后一点我要说明的是如何让访问XElement方法。让我们重写TryInvokeMember方法,以便将重定向所有方法调用它的XElement对象。当然,我这里使用System.Reflection命名空间。

public override bool TryInvokeMember(
    InvokeMemberBinder binder,     object[] args,     out object result)
{
    Type xmlType = typeof(XElement);
    try
    {
        result = xmlType.InvokeMember(
                  binder.Name,
                  BindingFlags.InvokeMethod |
                  BindingFlags.Public |
                  BindingFlags.Instance,
                  null, node, args);
        return true;
    }
    catch
    {
        result = null;
        return false;
    }
}

此方法使您能够调用任何DynamicXMLNode对象的节点XElement方法。最明显的缺点,这里是智能感知的情况下。
我什至会假装,这个例子是对LINQ的准备使用的包装,以XML库。它不支持属性,不允许你创建一个节点集合(例如,多个联系人),它可能是失踪的其他功能。创建一个库是一项艰巨的任务,并创造一个良好的包装太。但我希望在阅读这个博客帖子你可以自己创建一个DynamicObject充分运作的包装。
所以,如果你经常使用复杂的语法与检索的XML脚本文件或对象,或者如果您是自己建立这样一个库工程库,你应该考虑写一个包装。这样做可能让你更有效率和您的图书馆语法好得多。

 

 以上程序都在Visual Studio 2010 Beta 2中调试通过。


原文链接: http://blog.csdn.net/yincheng01/article/details/5602197

你可能感兴趣的:(基于Visual Studio2010与C#4.0新功能和展望--dynamic:用DynamicObject创建包装器)