{"id":239,"date":"2018-04-09T16:48:12","date_gmt":"2018-04-09T06:48:12","guid":{"rendered":"http:\/\/www.nerdhold.com\/coder\/?p=239"},"modified":"2019-06-07T20:40:10","modified_gmt":"2019-06-07T10:40:10","slug":"object-extend-linqpad","status":"publish","type":"post","link":"http:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/","title":{"rendered":"Object.Extend in C# for exploratory coding"},"content":{"rendered":"<p>LINQPad is great for exploratory coding. I use it all the time while I&#8217;m poking at APIs, and it&#8217;s completely replaced other scripting languages for me. I often find myself gradually building up result sets as I grope my way towards the result I&#8217;m looking for &#8211; and then I go back and re-factor it into something more presentable.<\/p>\n<p>Unfortunately, building up these result sets can mean copying all the members of an old dynamic object into a new dynamic object.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nvar sites = siteData.Select(s =&gt; new { SiteName = s&#x5B;0], SiteLink = s&#x5B;1] });\r\nvar siteInfo = sites.Select(s =&gt; new {\r\n    SiteName = s.SiteName, SiteLink = s.SiteLink, SiteInfo = SiteInformation&#x5B;s.SiteName]\r\n});\r\nvar siteContent = siteInfo.Select(s =&gt; {\r\n    var details = GenerateDetailsSomehow(s);\r\n    return new {\r\n        SiteName = s.SiteName,\r\n        SiteInfo = s.SiteInfo,\r\n        SiteLink = s.SiteLink,\r\n        SiteDetails = details\r\n    }\r\n});\r\n\/\/ ... more of the same\r\n<\/pre>\n<p>That gets tedious fast. Wouldn&#8217;t it be great if C# had something similar to JavaScript&#8217;s Object.Extend? Well, maybe it can. I jumped into the &#8220;My Extensions&#8221; file in LINQPad and put together the following extension method:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic static dynamic Extend(this object firstObj, params object&#x5B;] objs) {\r\n    var result = new ExpandoObject();\r\n    foreach (var o in new&#x5B;] { firstObj }.Union(objs)) {\r\n        foreach (var p in o.GetType().GetProperties().Select(p =&gt; new { Name = p.Name, Value = p.GetValue(o) })) {\r\n            var props = ((IDictionary&lt;string, object&gt;)result);\r\n            if (props.ContainsKey(p.Name)) props&#x5B;p.Name] = p.Value;\r\n            else props.Add(p.Name, p.Value);\r\n        }\r\n    }\r\n    return result;\r\n}\r\n<\/pre>\n<p>Now you can just call <code>.Extend(...)<\/code> on any object! So instead of having to create new objects all the time, you can do this:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nvar sites = siteData.Select(s =&gt; new { SiteName = s&#x5B;0], SiteLink = s&#x5B;1] });\r\nvar siteInfo = sites.Select(s =&gt; s.Extend(new {SiteInfo = SiteInformation&#x5B;s.SiteName]}));\r\nvar siteContent = siteInfo.Select(s =&gt;s.Extend(new { SiteDetails = GenerateDetailsSomehow(s) }));\r\n<\/pre>\n<p>That&#8217;s much easier to read (and quicker to write) than the first snippet! Unfortunately, it doesn&#8217;t work &#8211; the first call to our <code>object.Extend(...)<\/code> extension method is just fine, but the second call fails. Sadly, the way the runtime binder works means that our extension method won&#8217;t be available on the dynamics we create, so we can&#8217;t chain multiple calls using this approach.<br \/>\n<a href=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/RuntimeBinderException.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-350\" alt=\"RuntimeBinderException\" src=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/RuntimeBinderException.png\" width=\"860\" height=\"88\" srcset=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/RuntimeBinderException.png 860w, http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/RuntimeBinderException-300x30.png 300w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/a><\/p>\n<p>I have solved that (and a number of other minor annoyances) and put it all together in a Nuget package called <a title=\"ObjectExtend on Nuget\" href=\"https:\/\/www.nuget.org\/packages\/ObjectExtend\/\" target=\"_blank\">ObjectExtend<\/a>. Sadly, if you don&#8217;t have a license for LINQPad you may have to download the package and reference it manually, but if you do have a license you can use the Nuget client built right in to LINQPad.<\/p>\n<p>After adding ObjectExtend to our script, the chained script above works as expected:<br \/>\n<a href=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/ObjectExtendScreenshot.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-347\" alt=\"ObjectExtendScreenshot\" src=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/ObjectExtendScreenshot.png\" width=\"740\" height=\"475\" srcset=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/ObjectExtendScreenshot.png 740w, http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/ObjectExtendScreenshot-300x192.png 300w\" sizes=\"(max-width: 740px) 100vw, 740px\" \/><\/a><\/p>\n<p>There you have it! Object.Extend in C#.<\/p>\n<p>Please note this is a package focused on tinkering in LINQPad. It&#8217;s not the kind of thing you should be using while building maintainable, production-quality software.<\/p>\n<p>Update: I had a few requests for source code, and it&#8217;s up on github now, but rather than making people who want to understand it dig through code <a title=\"How my C# Object.Extend implementation works\" href=\"http:\/\/www.nerdhold.com\/coder\/2018\/04\/11\/how-csharp-object-extend-works\/\">I wrote up an explanation<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>LINQPad is great for exploratory coding. I use it all the time while I&#8217;m poking at APIs, and it&#8217;s completely replaced other scripting languages for me. I often find myself gradually building up result sets as I grope my way towards the result I&#8217;m looking for &#8211; and then I go back and re-factor it &hellip; <a href=\"http:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Object.Extend in C# for exploratory coding<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[18,13,12],"tags":[14],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.8 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Object.Extend in C# for exploratory coding - Nerdhold Coder<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Object.Extend in C# for exploratory coding - Nerdhold Coder\" \/>\n<meta property=\"og:description\" content=\"LINQPad is great for exploratory coding. I use it all the time while I&#8217;m poking at APIs, and it&#8217;s completely replaced other scripting languages for me. I often find myself gradually building up result sets as I grope my way towards the result I&#8217;m looking for &#8211; and then I go back and re-factor it &hellip; Continue reading Object.Extend in C# for exploratory coding &rarr;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/\" \/>\n<meta property=\"og:site_name\" content=\"Nerdhold Coder\" \/>\n<meta property=\"article:published_time\" content=\"2018-04-09T06:48:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-06-07T10:40:10+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/ObjectExtendScreenshot.png\" \/>\n<meta name=\"author\" content=\"Lionell Pack\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lionell Pack\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/\",\"url\":\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/\",\"name\":\"Object.Extend in C# for exploratory coding - Nerdhold Coder\",\"isPartOf\":{\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#website\"},\"datePublished\":\"2018-04-09T06:48:12+00:00\",\"dateModified\":\"2019-06-07T10:40:10+00:00\",\"author\":{\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\/\/www.nerdhold.com\/coder\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Object.Extend in C# for exploratory coding\"}]},{\"@type\":\"WebSite\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#website\",\"url\":\"http:\/\/www.nerdhold.com\/coder\/\",\"name\":\"Nerdhold Coder\",\"description\":\"Tinkerings of a C# Coder\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"http:\/\/www.nerdhold.com\/coder\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77\",\"name\":\"Lionell Pack\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/image\/\",\"url\":\"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg\",\"contentUrl\":\"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg\",\"caption\":\"Lionell Pack\"},\"sameAs\":[\"http:\/\/blog.rophuine.net\"],\"url\":\"http:\/\/www.nerdhold.com\/coder\/author\/admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Object.Extend in C# for exploratory coding - Nerdhold Coder","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/","og_locale":"en_US","og_type":"article","og_title":"Object.Extend in C# for exploratory coding - Nerdhold Coder","og_description":"LINQPad is great for exploratory coding. I use it all the time while I&#8217;m poking at APIs, and it&#8217;s completely replaced other scripting languages for me. I often find myself gradually building up result sets as I grope my way towards the result I&#8217;m looking for &#8211; and then I go back and re-factor it &hellip; Continue reading Object.Extend in C# for exploratory coding &rarr;","og_url":"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/","og_site_name":"Nerdhold Coder","article_published_time":"2018-04-09T06:48:12+00:00","article_modified_time":"2019-06-07T10:40:10+00:00","og_image":[{"url":"http:\/\/www.nerdhold.com\/coder\/files\/2018\/04\/ObjectExtendScreenshot.png"}],"author":"Lionell Pack","twitter_misc":{"Written by":"Lionell Pack","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/","url":"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/","name":"Object.Extend in C# for exploratory coding - Nerdhold Coder","isPartOf":{"@id":"http:\/\/www.nerdhold.com\/coder\/#website"},"datePublished":"2018-04-09T06:48:12+00:00","dateModified":"2019-06-07T10:40:10+00:00","author":{"@id":"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77"},"breadcrumb":{"@id":"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.nerdhold.com\/coder\/2018\/04\/09\/object-extend-linqpad\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/www.nerdhold.com\/coder\/"},{"@type":"ListItem","position":2,"name":"Object.Extend in C# for exploratory coding"}]},{"@type":"WebSite","@id":"http:\/\/www.nerdhold.com\/coder\/#website","url":"http:\/\/www.nerdhold.com\/coder\/","name":"Nerdhold Coder","description":"Tinkerings of a C# Coder","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"http:\/\/www.nerdhold.com\/coder\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77","name":"Lionell Pack","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/image\/","url":"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg","contentUrl":"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg","caption":"Lionell Pack"},"sameAs":["http:\/\/blog.rophuine.net"],"url":"http:\/\/www.nerdhold.com\/coder\/author\/admin\/"}]}},"_links":{"self":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts\/239"}],"collection":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/comments?post=239"}],"version-history":[{"count":13,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts\/239\/revisions"}],"predecessor-version":[{"id":462,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts\/239\/revisions\/462"}],"wp:attachment":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/media?parent=239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/categories?post=239"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/tags?post=239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}