{"id":3003,"date":"2009-10-19T14:24:00","date_gmt":"2009-10-19T14:24:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudio\/2009\/10\/19\/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject\/"},"modified":"2019-02-14T15:42:19","modified_gmt":"2019-02-14T23:42:19","slug":"dynamic-in-c-4-0-creating-wrappers-with-dynamicobject","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject\/","title":{"rendered":"Dynamic in C# 4.0: Creating Wrappers with DynamicObject"},"content":{"rendered":"<p>In the <a href=\"http:\/\/blogs.msdn.com\/csharpfaq\/archive\/2009\/10\/01\/dynamic-in-c-4-0-introducing-the-expandoobject.aspx\">previous post<\/a> I showed how you can use the new dynamic feature and the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.expandoobject(VS.100).aspx\">ExpandoObject<\/a> class to add and remove properties at run time, and how this can make your code more readable and flexible than code written with LINQ to XML syntax. <\/p>\n<p>But there were some obvious flaws in that example: While <b>ExpandoObject<\/b> provided better syntax, LINQ to XML provided a lot of useful library methods that helped you to work with XML files. So, is it possible to combine those two advantages, to have better syntax and still get all those methods? The answer is yes, but you need another type from the <b>System.Dynamic<\/b> namespace: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject(VS.100).aspx\">DynamicObject<\/a><b>.<\/b><\/p>\n<p>The <b>DynamicObject<\/b> class enables you to override operations like getting or setting a member, calling a method, or performing any binary, unary, or type conversion operation. To illustrate the issue, let\u2019s create a very simple object that overrides the \u201cget property\u201d operation, so that whenever you access a property it returns the property\u2019s name as a string. This example has no practical value.<\/p>\n<pre class=\"code\"><span>public class <\/span><span>SampleObject <\/span>: <span>DynamicObject\n<\/span>{\n    <span>public override bool <\/span>TryGetMember(\n        <span>GetMemberBinder <\/span>binder, <span>out object <\/span>result)\n    {\n        result = binder.Name;\n        <span>return true<\/span>;\n    }\n}<\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>As with <b>ExpandoObject<\/b>, we must use the <b>dynamic<\/b> keyword to create an instance of this class.<\/p>\n<pre class=\"code\"><span>dynamic <\/span>obj = <span>new <\/span><span>SampleObject<\/span>();\n<span>Console<\/span>.WriteLine(obj.SampleProperty);\n<span>\/\/Prints \"SampleProperty\".<\/span><\/pre>\n<p>Let\u2019s see what\u2019s going on in this example. When you call <font face=\"Courier New\">obj.SampleProperty<\/font>, the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd233052(VS.100).aspx\">dynamic language runtime<\/a> (DLR) uses the language binder to look for a static definition of this property in the <b>SampleObject<\/b> class. If there is no such property, the DLR calls the <b>TryGetMember<\/b> method. This method gets information about what property it was called for through the <font face=\"Courier New\">binder<\/font> parameter. As you can see, the <font face=\"Courier New\">binder.Name<\/font> contains the actual name of the property.<\/p>\n<p>The <b>TryGetMember<\/b> method returns <font face=\"Courier New\">true<\/font> if the operation is successful. But the actual result of the operation must be assigned to the out parameter <font face=\"Courier New\">result<\/font>. In this example, <b>TryGetMember<\/b> returns <font face=\"Courier New\">true<\/font>, but <font face=\"Courier New\">obj.SampleProperty<\/font> returns &#8220;SampleProperty&#8221;. <\/p>\n<p>Now let\u2019s move to a more complex example and create a wrapper for the <b>XElement<\/b> object. Once again, I\u2019ll try to provide better syntax for the following LINQ to XML sample.<\/p>\n<pre class=\"code\"><span>XElement <\/span>contactXML =\n    <span>new <\/span><span>XElement<\/span>(<span>\"Contact\"<\/span>,\n    <span>new <\/span><span>XElement<\/span>(<span>\"Name\"<\/span>, <span>\"Patrick Hines\"<\/span>),\n    <span>new <\/span><span>XElement<\/span>(<span>\"Phone\"<\/span>, <span>\"206-555-0144\"<\/span>),\n    <span>new <\/span><span>XElement<\/span>(<span>\"Address\"<\/span>,\n        <span>new <\/span><span>XElement<\/span>(<span>\"Street1\"<\/span>, <span>\"123 Main St\"<\/span>),\n        <span>new <\/span><span>XElement<\/span>(<span>\"City\"<\/span>, <span>\"Mercer Island\"<\/span>),\n        <span>new <\/span><span>XElement<\/span>(<span>\"State\"<\/span>, <span>\"WA\"<\/span>),\n        <span>new <\/span><span>XElement<\/span>(<span>\"Postal\"<\/span>, <span>\"68042\"<\/span>)\n    )\n);<\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>First of all, I need to create an analog of <b>ExpandoObject<\/b>. I still want to be able to dynamically add and remove properties. But since I am essentially creating a wrapper for the <b>XElement<\/b> type, I\u2019ll use <b>XElement<\/b> instead of the dictionary to maintain the properties.<\/p>\n<pre class=\"code\"><span>public class <\/span><span>DynamicXMLNode <\/span>: <span>DynamicObject\n<\/span>{\n    <span>XElement <\/span>node;\n    <span>public <\/span>DynamicXMLNode(<span>XElement <\/span>node)\n    {\n        <span>this<\/span>.node = node;\n    }\n    <span>public <\/span>DynamicXMLNode()\n    {\n    }\n    <span>public <\/span>DynamicXMLNode(<span>String <\/span>name)\n    {\n        node = <span>new <\/span><span>XElement<\/span>(name);\n    }\n    <span>public override bool <\/span>TrySetMember(\n        <span>SetMemberBinder <\/span>binder, <span>object <\/span>value)\n    {\n        <span>XElement <\/span>setNode = node.Element(binder.Name);\n        <span>if <\/span>(setNode != <span>null<\/span>)\n            setNode.SetValue(value);\n        <span>else\n        <\/span>{\n            <span>if <\/span>(value.GetType() == <span>typeof<\/span>(<span>DynamicXMLNode<\/span>))\n                node.Add(<span>new <\/span><span>XElement<\/span>(binder.Name));\n            <span>else\n                <\/span>node.Add(<span>new <\/span><span>XElement<\/span>(binder.Name, value));\n        }\n        <span>return true<\/span>;\n    }\n    <span>public override bool <\/span>TryGetMember(<br \/>        <span>GetMemberBinder <\/span>binder, <span>out object <\/span>result)\n    {\n        <span>XElement <\/span>getNode = node.Element(binder.Name);\n        <span>if <\/span>(getNode != <span>null<\/span>)\n        {\n            result = <span>new <\/span><span>DynamicXMLNode<\/span>(getNode);\n            <span>return true<\/span>;\n        }\n        <span>else\n        <\/span>{\n            result = <span>null<\/span>;\n            <span>return false<\/span>;\n        }\n    }\n}<\/pre>\n<p>And here is how you can use this class.<\/p>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<pre class=\"code\"><span>dynamic <\/span>contact = <span>new <\/span><span>DynamicXMLNode<\/span>(<span>\"Contacts\"<\/span>);\ncontact.Name = <span>\"Patrick Hines\"<\/span>;\ncontact.Phone = <span>\"206-555-0144\"<\/span>;\ncontact.Address = <span>new <\/span><span>DynamicXMLNode<\/span>();\ncontact.Address.Street = <span>\"123 Main St\"<\/span>;\ncontact.Address.City = <span>\"Mercer Island\"<\/span>;\ncontact.Address.State = <span>\"WA\"<\/span>;\ncontact.Address.Postal = <span>\"68402\"<\/span>;<\/pre>\n<p>Let\u2019s look at the <font face=\"Courier New\">contact<\/font> object. When this object is created, it initializes its inner <b>XElement<\/b>. If you set a property value, like in <font face=\"Courier New\">contact.Phone = <span>&#8220;206-555-0144&#8221;<\/span><\/font>, the <b>TrySetMember<\/b> method checks whether an element with the name Phone exists in its <b>XElement<\/b>. If it does not exist, the method creates the element.<\/p>\n<p>The next interesting line is <font face=\"Courier New\">contact.Address = <span>new <\/span><span>DynamicXMLNode<\/span>().<\/font> Basically, in this particular example this line means that I want to create a node that has subnodes. For this property, the <b>TrySetMember<\/b> method creates an <b>XElement<\/b> without a value.<\/p>\n<p>The most complex case here is a line such as <font face=\"Courier New\">contact.Address.State = <font color=\"#000000\"><span>&#8220;WA&#8221;<\/span>.<\/font><\/font> First, the <b>TryGetMember<\/b> method is called for <font face=\"Courier New\">contact.Address<\/font> and returns a new <b>DynamicXMLNode<\/b> object, which is initialized by the <b>XElement<\/b> with the name Address. (Theoretically, I could have returned the <b>XElement<\/b> itself, but that would make the example more complicated.) Then the <b>TrySetMember<\/b> method is called. The method looks for the State element in <font face=\"Courier New\">contact.Address<\/font>. If it doesn\u2019t find one, it creates it.<\/p>\n<p>So I have successfully created the required XML structure. But <b>TryGetMember<\/b> always returns an instance of <b>DynamicXMLNode<\/b>. How do I get the actual value of the XML node? For example, I want the following line to work, but now it throws an exception.<\/p>\n<pre class=\"code\"><span>String <\/span>state = contact.Address.State;<\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>I have several options here. I can modify the <b>TryGetMember<\/b> method to return actual values for leaf nodes, for example. But let\u2019s explore another option: override type conversion. Just add the following method to the <b>DynamicXMLNode<\/b> class.<\/p>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<pre class=\"code\"><span>public override bool <\/span>TryConvert(\n    <span>ConvertBinder <\/span>binder, <span>out object <\/span>result)\n{\n    <span>if <\/span>(binder.Type == <span>typeof<\/span>(<span>String<\/span>))\n    {\n        result = node.Value;\n        <span>return true<\/span>;\n    }\n    <span>else\n    <\/span>{\n        result = <span>null<\/span>;\n        <span>return false<\/span>;\n    }\n}<\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>Now whenever I have an explicit or implicit type conversion of the <b>DynamicXMLNode<\/b> type, the <b>TryConvert<\/b> method is called. The method checks what type the object is converted to and, if this type is <b>String<\/b>, the method returns the value of the inner <b>XElement<\/b>. Otherwise, it returns <b>false<\/b>, which means that the language should determine what to do next (in most cases it means that you\u2019re going to get a run-time exception).<\/p>\n<p>The last thing I\u2019m going to show is how to get access to the <b>XElement<\/b> methods. Let\u2019s override the <b>TryInvokeMember<\/b> method so that it will redirect all the method calls to its <b>XElement<\/b> object. Of course, I\u2019m using the <strong>System.Reflection<\/strong> namespace here.<\/p>\n<pre class=\"code\"><span>public override bool <\/span>TryInvokeMember(\n    <span>InvokeMemberBinder <\/span>binder, <br \/>    <span>object<\/span>[] args, <br \/>    <span>out object <\/span>result)\n{\n    <span>Type <\/span>xmlType = <span>typeof<\/span>(<span>XElement<\/span>);\n    <span>try\n    <\/span>{\n        result = xmlType.InvokeMember(\n                  binder.Name,\n                  BindingFlags.InvokeMethod |\n                  BindingFlags.Public |\n                  BindingFlags.Instance,\n                  <span>null<\/span>, node, args);\n        <span>return true<\/span>;\n    }\n    <span>catch\n    <\/span>{\n        result = <span>null<\/span>;\n        <span>return false<\/span>;\n    }\n}<\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>This method enables you to call <b>XElement<\/b> methods on any node of the <b>DynamicXMLNode<\/b> object. The most obvious drawback here is absence of IntelliSense.<\/p>\n<p>I\u2019m not even going to pretend that this example is a ready-to-use wrapper for the LINQ to XML library. It doesn\u2019t support attributes, doesn\u2019t allow you to create a collection of nodes (for example, multiple contacts), and it is probably missing other features. Creating a library is a difficult task, and creating a good wrapper is too. But I hope that after reading this blog post you can create a fully functioning wrapper with <b>DynamicObject<\/b> yourself.<\/p>\n<p>So, if you routinely use a library with complicated syntax that crawls XML files or works with script objects, or if you are creating such a library yourself, you should probably consider writing a wrapper. Doing this might make you more productive and the syntax of your library much better.<\/p>\n<p>All the examples provided in this blog post work in the just released <a><\/a><a href=\"http:\/\/msdn.microsoft.com\/en-us\/vstudio\/dd582936.aspx\">Visual Studio 2010 Beta 2<\/a>. If you have any comments or suggestions, you are welcome to post them here or contact the DLR team at <a href=\"http:\/\/www.codeplex.com\/dlr\">http:\/\/www.codeplex.com\/dlr<\/a>. You can also send an e-mail to the DLR team at <a href=\"mailto:dlr@microsoft.com\">dlr@microsoft.com<\/a>.<\/p>\n<p><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject(VS.100).aspx\">Documentation for <strong>DynamicObject<\/strong><\/a> is also available on MSDN (check out our new MSDN design and don\u2019t forget to take a look at the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject(VS.100,lightweight).aspx\">lightweight view<\/a>.) In documentation, you can read about other useful methods of this class, such as <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject.trybinaryoperation(VS.100).aspx\">TryBinaryOperation<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject.tryunaryoperation(VS.100).aspx\">TryUnaryOperation<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject.trysetindex(VS.100).aspx\">TrySetIndex<\/a>, and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.dynamic.dynamicobject.trygetindex(VS.100).aspx\">TryGetIndex<\/a>. <\/p>\n<p><img decoding=\"async\" src=\"\" width=\"1\" height=\"1\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post I showed how you can use the new dynamic feature and the ExpandoObject class to add and remove properties at run time, and how this can make your code more readable and flexible than code written with LINQ to XML syntax. But there were some obvious flaws in that example: While [&hellip;]<\/p>\n","protected":false},"author":13,"featured_media":255385,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[155],"tags":[3,1383,185,13],"class_list":["post-3003","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-visual-studio","tag-net-framework","tag-c","tag-node-js","tag-visual-studio-2010"],"acf":[],"blog_post_summary":"<p>In the previous post I showed how you can use the new dynamic feature and the ExpandoObject class to add and remove properties at run time, and how this can make your code more readable and flexible than code written with LINQ to XML syntax. But there were some obvious flaws in that example: While [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/3003","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/13"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=3003"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/3003\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/255385"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=3003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=3003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=3003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}