{"id":585,"date":"2012-12-20T11:05:18","date_gmt":"2012-12-20T11:05:18","guid":{"rendered":"http:\/\/www.themissingdocs.net\/wordpress\/?p=585"},"modified":"2012-12-20T11:05:18","modified_gmt":"2012-12-20T11:05:18","slug":"enums-in-dictionary-keys","status":"publish","type":"post","link":"https:\/\/www.themissingdocs.net\/?p=585","title":{"rendered":"Enums in dictionary keys."},"content":{"rendered":"<p>I was doing some random reflection walking to remind myself whether using a struct as a dictionary key triggered any boxing operations using the default equality comparer.\u00a0 And if you implement IEquatable&lt;T&gt; it still executes a box statement in IL, but my attempts at creating an equality comparer which trivially does not execute boxing operations isn&#8217;t any faster, so it seems the optimiser can properly eliminate explicit null checks in generics where the type parameter ends up being a struct and remove the box operation as dead code.\u00a0 But that is not what this post is about.<\/p>\n<p>Along the way in my reflection I saw that the default equality comparer special cases enums.\u00a0 I immediately thought this was great, because I had recently discovered that GetHashCode called on an enum is &#8216;expensive&#8217;, it is *much* cheaper if you cast it to the underlying type and call GetHashCode on that instead. (Specific case was an enum member of a type which overrode equality, so the default equality comparer special case didn&#8217;t apply.)\u00a0 So I had a look at the reflected code for this special case enum equality comparer and found an &#8216;interesting&#8217; thing.<\/p>\n<p>The implementation called this thing called JitHelper.UnsafeEnumCast&lt;T&gt; to cast its parameters to int.\u00a0 What makes it interesting is that the &#8216;implementation&#8217; of this method is &#8216;throw new InvalidOperationException()&#8217;.\u00a0 Wait what&#8230;<\/p>\n<p>Now obviously this method implementation isn&#8217;t actually called by EnumEqualityComparer, using enum&#8217;s as dictionary keys works just fine.\u00a0 So it seems that the JIT magically substitutes a cast if T is an enum type, and only uses the implementation as a default fallback scenario.<\/p>\n<p>The things they do to get around the limited type constraints in generics&#8230;<\/p>\n<p>And this led me to look closer at JitHelper.\u00a0 Here I find JitHelper.UnsafeCast&lt;T&gt;&#8230;<\/p>\n<p>Have you ever wanted to cast Generic&lt;Specific&gt; to Generic&lt;T&gt; when you know at runtime that T is Specific?\u00a0 It seems that this little piece of magic lets you do that. (It is apparently used in async stuff to allow for cached result tasks to be returned for a bunch of basic types from a generic result task building function.\u00a0 For bonus points I now know that true, false, 0 in a dozen types, null and the integers -1 through 9(?!?), each have their own special cached task instances to be shared&#8230;)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was doing some random reflection walking to remind myself whether using a struct as a dictionary key triggered any boxing operations using the default equality comparer.\u00a0 And if you implement IEquatable&lt;T&gt; it still executes a box statement in IL, but my attempts at creating an equality comparer which trivially does not execute boxing operations &hellip; <a href=\"https:\/\/www.themissingdocs.net\/?p=585\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Enums in dictionary keys.<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-585","post","type-post","status-publish","format-standard","hentry","category-net-stuff"],"_links":{"self":[{"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=\/wp\/v2\/posts\/585","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=585"}],"version-history":[{"count":0,"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=\/wp\/v2\/posts\/585\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=585"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=585"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.themissingdocs.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=585"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}