
{"id":4552,"date":"2019-05-10T21:59:28","date_gmt":"2019-05-11T01:59:28","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=4552"},"modified":"2019-05-10T22:08:26","modified_gmt":"2019-05-11T02:08:26","slug":"c-how-to-shoot-yourself-in-the-foot-with-a-generic-extension-method","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=4552","title":{"rendered":"C#: How to shoot yourself in the foot with a generic extension method"},"content":{"rendered":"\n<p><strong>TL;DR<\/strong>: wrapping existing method <code>DoSomething(MyData data)<\/code>, with an identically named generic extension method <code>DoSomething&lt;T>(T data)<\/code> is a bad idea, since it erases compile-time type checking. <code>DoSomething()<\/code> may now be called with any type, and it may be hard to tell whether you are calling the original or the wrapper.<\/p>\n\n\n\n<p>The following is a true story, even though some details have been changed to protect the innocent. Suppose we have a messaging library along these lines:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><span style=\"color:blue;\">interface<\/span>&nbsp;<span style=\"color:#2b91af;\">IMessage<\/span>\n{\n&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:#2b91af;\">Type<\/span>&nbsp;Type&nbsp;{&nbsp;<span style=\"color:blue;\">get<\/span>;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:blue;\">object<\/span>&nbsp;Data&nbsp;{&nbsp;<span style=\"color:blue;\">get<\/span>;&nbsp;}\n}\n\n<span style=\"color:blue;\">class<\/span>&nbsp;<span style=\"color:#2b91af;\">Message<\/span>&nbsp;:&nbsp;<span style=\"color:#2b91af;\">IMessage<\/span>\n{\n&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:blue;\">public<\/span>&nbsp;<span style=\"color:#2b91af;\">Type<\/span>&nbsp;Type&nbsp;{&nbsp;<span style=\"color:blue;\">get<\/span>;&nbsp;<span style=\"color:blue;\">set<\/span>;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:blue;\">public<\/span>&nbsp;<span style=\"color:blue;\">object<\/span>&nbsp;Data&nbsp;{&nbsp;<span style=\"color:blue;\">get<\/span>;&nbsp;<span style=\"color:blue;\">set<\/span>;&nbsp;}\n}\n\n<span style=\"color:blue;\">class<\/span>&nbsp;<span style=\"color:#2b91af;\">Sender<\/span>\n{\n&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:blue;\">public<\/span>&nbsp;<span style=\"color:blue;\">void<\/span>&nbsp;Send(<span style=\"color:#2b91af;\">Message<\/span>&nbsp;message)\n&nbsp;&nbsp;&nbsp;&nbsp;{\n<span style=\"color:green;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;...do&nbsp;actual&nbsp;sending&nbsp;here...<\/span>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:#2b91af;\">Console<\/span>.WriteLine(<span style=\"color:#a31515;\">$&quot;Send:&nbsp;Type=<\/span>{message.Type.Name}<span style=\"color:#a31515;\">,&nbsp;Data=<\/span>{message.Data}<span style=\"color:#a31515;\">&quot;<\/span>);\n&nbsp;&nbsp;&nbsp;&nbsp;}\n}\n<\/pre>\n\n\n\n<p>Expressions like <code>sender.Send(new Message {Type = typeof(int), Data = 42})<\/code> quickly become an annoyance, so we create an extension method to simplify them:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><span style=\"color:blue;\">static<\/span>&nbsp;<span style=\"color:blue;\">class<\/span>&nbsp;<span style=\"color:#2b91af;\">SenderExtensions<\/span>\n{\n&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color:blue;\">public<\/span>&nbsp;<span style=\"color:blue;\">static<\/span>&nbsp;<span style=\"color:blue;\">void<\/span>&nbsp;Send&lt;<span style=\"color:#2b91af;\">T<\/span>&gt;(<span style=\"color:blue;\">this<\/span>&nbsp;<span style=\"color:#2b91af;\">Sender<\/span>&nbsp;sender,&nbsp;<span style=\"color:#2b91af;\">T<\/span>&nbsp;data)\n&nbsp;&nbsp;&nbsp;&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sender.Send(<span style=\"color:blue;\">new<\/span>&nbsp;<span style=\"color:#2b91af;\">Message<\/span>&nbsp;{Type&nbsp;=&nbsp;<span style=\"color:blue;\">typeof<\/span>(<span style=\"color:#2b91af;\">T<\/span>),&nbsp;Data&nbsp;=&nbsp;data});\n&nbsp;&nbsp;&nbsp;&nbsp;}\n}\n<\/pre>\n\n\n\n<p>Now we can simply write <code>sender.Send(42)<\/code>and it works as expected. But&#8230; Consider this code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><span style=\"color:blue;\">var<\/span>&nbsp;message&nbsp;=&nbsp;<span style=\"color:blue;\">new<\/span>&nbsp;<span style=\"color:#2b91af;\">Message<\/span>&nbsp;{Type&nbsp;=&nbsp;<span style=\"color:blue;\">typeof<\/span>(<span style=\"color:blue;\">int<\/span>),&nbsp;Data&nbsp;=&nbsp;42};\nsender.Send(message);&nbsp;<span style=\"color:green;\">\/\/&nbsp;prints&nbsp;Send:&nbsp;Type=Int32,&nbsp;Data=42<\/span>\n\n<span style=\"color:blue;\">var<\/span>&nbsp;iMessage&nbsp;=&nbsp;(<span style=\"color:#2b91af;\">IMessage<\/span>)&nbsp;message;\nsender.Send(iMessage);&nbsp;<span style=\"color:green;\">\/\/&nbsp;oops!&nbsp;Type=IMessage,&nbsp;Data=ShootFoot1.Message<\/span>\n<\/pre>\n\n\n\n<p>We cast our message to <code>IMessage<\/code> type, but the <code>Send()<\/code> method expects parameter of type <code>Message<\/code>. This by itself is probably a design error, but let&#8217;s ignore it. \u00a1No problemo! says the compiler. Passing <code>IMessage<\/code> compiles, but it calls the extension method instead of the regular one, and wraps our message in another <code>Message<\/code> object.<\/p>\n\n\n\n<p>The same would have happened if we had a generic overload of <code>Send()<\/code>, but extension method is especially evil, since it is invisilbe. You cannot guess its existence by looking at either the call site or the definition of the <code>Sender<\/code> class. I personally marveled at the wrapped message in debugger for at least 10 minutes before I realized what&#8217;s going on. Obviously, the real program was much more complex and had many additional details that caused distraction.<\/p>\n\n\n\n<p>Moral of the story: if you create a generic extension method like this, give it a distinct name. E.g. if the extension method were called <code>SendObject()<\/code>, then <code>Send(iMessage)<\/code>would simply have not compiled.<br><\/p>\n\n\n\n<p><strong>Complete code:<\/strong><br><a href=\"https:\/\/raw.githubusercontent.com\/ikriv\/RandomSamples\/69e8c91b572da45934a1fbbfdcc482c2ac5eb0db\/ShootFoot1\/Program.cs\">The original, buggy version<\/a><br><a href=\"https:\/\/raw.githubusercontent.com\/ikriv\/RandomSamples\/87618a5c32c8d2c61483c87311c17ba8a54bfb66\/ShootFoot1\/Program.cs\">Fixed version with renamed extension method<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR: wrapping existing method DoSomething(MyData data), with an identically named generic extension method DoSomething&lt;T>(T data) is a bad idea, since it erases compile-time type checking. DoSomething() may now be called <a href=\"https:\/\/ikriv.com\/blog\/?p=4552\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[3,4],"tags":[],"class_list":["entry","author-ikriv","post-4552","post","type-post","status-publish","format-standard","category-dotnet","category-hack"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4552","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4552"}],"version-history":[{"count":18,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4552\/revisions"}],"predecessor-version":[{"id":4570,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4552\/revisions\/4570"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4552"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4552"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4552"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}