{"id":1155,"date":"2018-03-30T11:53:11","date_gmt":"2018-03-30T03:53:11","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/seteplia\/?p=1155"},"modified":"2019-06-11T21:54:03","modified_gmt":"2019-06-12T04:54:03","slug":"nullable-types-arithmetic-and-null-coalescing-operator-precedence","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/nullable-types-arithmetic-and-null-coalescing-operator-precedence\/","title":{"rendered":"Nullable types arithmetic and null-coalescing operator precedence"},"content":{"rendered":"<p>Here is a simple question for you: which version of a <code>GetHashCode()<\/code> is correct and what performance impact does the incorrect version have?<\/p>\n<pre class=\"lang:default decode:true \">public struct Struct1\r\n{\r\n    public int N { get; }\r\n    public string S { get; }\r\n    public Struct1(int n, string s = null) { N = n; S = s; }\r\n\r\n    public override int GetHashCode() =&gt; \r\n        N ^ \r\n        S?.GetHashCode() ?? 0;\r\n\r\n    public override bool Equals(object obj) =&gt; \r\n        obj is Struct1 other &amp;&amp; N == other.N &amp;&amp; string.Equals(S, other.S);\r\n}\r\n\r\npublic struct Struct2\r\n{\r\n    public int N { get; }\r\n    public string S { get; }\r\n    public Struct2(int n, string s = null) { N = n; S = s; }\r\n\r\n    public override int GetHashCode() =&gt; \r\n        S?.GetHashCode() ?? 0 ^\r\n        N;\r\n\r\n    public override bool Equals(object obj) =&gt; \r\n        obj is Struct1 other &amp;&amp; N == other.N &amp;&amp; string.Equals(S, other.S);\r\n}<\/pre>\n<p>The structs are not perfect (they don&#8217;t implement <code>IEquatable&lt;T&gt;<\/code>) but this is not the point. The only difference between the two is the <code>GetHashCode()<\/code> implementation:<\/p>\n<pre class=\"lang:default decode:true \">\/\/ Struct 1\r\npublic override int GetHashCode() =&gt;\r\n    N ^\r\n    S?.GetHashCode() ?? 0;\r\n\r\n\/\/ Struct 2\r\npublic override int GetHashCode() =&gt;\r\n    S?.GetHashCode() ?? 0 ^\r\n    N;<\/pre>\n<p>Let&#8217;s check the behavior using the following simple benchmark:<\/p>\n<pre class=\"lang:default decode:true \">private const int count = 10000;\r\nprivate static Struct1[] _arrayStruct1 =\r\n    Enumerable.Range(1, count).Select(n =&gt; new Struct1(n)).ToArray();\r\nprivate static Struct2[] _arrayStruct2 =\r\n    Enumerable.Range(1, count).Select(n =&gt; new Struct2(n)).ToArray();\r\n\r\n[Benchmark]\r\npublic int Struct1() =&gt; new HashSet&lt;Struct1&gt;(_arrayStruct1).Count;\r\n\r\n[Benchmark]\r\npublic int Struct2() =&gt; new HashSet&lt;Struct2&gt;(_arrayStruct2).Count;<\/pre>\n<p>The results are:<\/p>\n<pre class=\"lang:default decode:true\">  Method |         Mean |        Error |       StdDev | \r\n-------- |-------------:|-------------:|-------------:| \r\n Struct1 | 736,298.4 us | 4,224.637 us | 3,745.030 us | \r\n Struct2 |     353.8 us |     2.382 us |     1.989 us |<\/pre>\n<p>Wow! The <code>Struct2<\/code> is 2000 times faster! This definitely means that the second implementation is correct and the first one is not! Right? Actually, not.<\/p>\n<p>Both implementations are incorrect and just by an accident the second one &#8220;works better&#8221; in this particular case. Let&#8217;s take closer look at the <code>GetHashCode<\/code> method for <code>Struct1<\/code>:<\/p>\n<pre class=\"lang:default decode:true \">public override int GetHashCode() =&gt; N ^ S?.GetHashCode() ?? 0;<\/pre>\n<p>You may think that this statement is equivalent to <code>N ^ (S?.GetHashCode() ?? 0)<\/code>but it is actually equivalent to <code>(N ^ S?.GetHashCode()) ?? 0<\/code>:<\/p>\n<pre class=\"lang:default decode:true \">public override int GetHashCode()\r\n{\r\n    int? num = N ^ ((S != null) ? new int?(S.GetHashCode()) : null);\r\n\r\n    if (num == null)\r\n        return 0;\r\n\r\n    return num.GetValueOrDefault();\r\n}<\/pre>\n<p>Now it is way more obvious why the <code>Struct1<\/code> is so slow: when <code>S<\/code> property is <code>null<\/code>(which is always the case in this example), the hash code is <code>0<\/code> regardless of the <code>N<\/code>because <code>N ^ (int?)null<\/code> is <code>null<\/code>. And trying to add 10000 values with the same hash code effectively converts the hash set into a linked list drastically affecting the performance.<\/p>\n<p>But the second implementation is also wrong:<\/p>\n<pre class=\"lang:default decode:true\">public override int GetHashCode() =&gt; S?.GetHashCode() ?? 0 ^ N;<\/pre>\n<p>Is equivalent to:<\/p>\n<pre class=\"lang:default decode:true \">public override int GetHashCode()\r\n{\r\n    if (S == null)\r\n    {\r\n        return 0 ^ N;\r\n    }\r\n\r\n    return S.GetHashCode();\r\n}<\/pre>\n<p>In this particular case, this implementation gives us way better distribution, but just because the <code>S<\/code> is always null. In other scenarios, this hash function could be terrible and could give the same value for a large set of instances as well.<\/p>\n<h4>Conclusion<\/h4>\n<p>There are two reasons why the expression <code>N ^ S?.GetHashCode()??0<\/code> gives us not what we could expect. C# supports the notion of lifted operators that allows mixing nullable and non-nullable values together in one expression: <code>42 ^ (int?)null<\/code> is <code>null<\/code>. Second, the priority of null-coalescing operator (<code>??<\/code>) is lower than the priority of <code>^<\/code>.<\/p>\n<p>Operator precedence for some operators is so obvious that we can omit explicit parens around them. In case of the null-coalescing operator, the precedence could be tricky so use parenthesis to clarify your meaning.<\/p>\n<h4>Additional references<\/h4>\n<ul>\n<li><a href=\"https:\/\/blogs.msdn.microsoft.com\/ericlippert\/2007\/06\/27\/what-exactly-does-lifted-mean\/\">What exactly does &#8216;lifted&#8217; mean?<\/a> by Eric Lippert<\/li>\n<li><a href=\"https:\/\/stackoverflow.com\/questions\/3370110\/what-are-lifted-operators\">What are lifted operators?<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Here is a simple question for you: which version of a GetHashCode() is correct and what performance impact does the incorrect version have?<\/p>\n","protected":false},"author":4004,"featured_media":37840,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6699],"tags":[6695],"class_list":["post-1155","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c","tag-seteplia"],"acf":[],"blog_post_summary":"<p>Here is a simple question for you: which version of a GetHashCode() is correct and what performance impact does the incorrect version have?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/1155","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/4004"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=1155"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/1155\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=1155"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=1155"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=1155"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}