<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>C# on Jéf Bueno</title><link>https://jfbueno.github.io/tags/c%23/</link><description>Recent content in C# on Jéf Bueno</description><generator>Hugo -- gohugo.io</generator><language>pt-br</language><lastBuildDate>Tue, 22 Mar 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://jfbueno.github.io/tags/c%23/index.xml" rel="self" type="application/rss+xml"/><item><title>Diferença entre 'x is null' e 'x == null'</title><link>https://jfbueno.github.io/p/diferen%C3%A7a-entre-x-is-null-e-x-null/</link><pubDate>Tue, 22 Mar 2022 00:00:00 +0000</pubDate><guid>https://jfbueno.github.io/p/diferen%C3%A7a-entre-x-is-null-e-x-null/</guid><description>&lt;h2 id="pattern-matching-em-c">Pattern matching em C#
&lt;/h2>&lt;p>As novas versões do C# trouxeram diversas novidades que melhoraram o nosso dia a dia escrevendo código.&lt;/p>
&lt;p>A partir do C# 7 começaram a surgir recursos voltados para &lt;a class="link" href="https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching" target="_blank" rel="noopener"
>&lt;em>pattern matching&lt;/em>&lt;/a>. Um recurso importante são as &lt;a class="link" href="https://devblogs.microsoft.com/dotnet/new-features-in-c-7-0/#is-expressions-with-patterns" target="_blank" rel="noopener"
>expressões do tipo &lt;code>is&lt;/code> com padrões&lt;/a>. Esse tipo de expressão tem diversas variações, mas eu vejo uma em particular causar um pouco de confusão em pessoas que não estão tão acostumadas com C# moderno.&lt;/p>
&lt;p>Atualmente, validações de nulabilidade podem ser feitas assim&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ou assim&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="p">==&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Como tenho costume de escrever na primeira forma geralmente me perguntam qual a diferença entre as duas. Neste post tento explicar a diferença entre as duas comparações fazendo a análise do código IL gerado para os dois casos.&lt;/p>
&lt;blockquote>
&lt;p>🛑 &lt;strong>Spoiler&lt;/strong> (ou &lt;strong>TL;DR&lt;/strong>): não há diferença nenhuma na maioria dos casos, o cenário muda quando é feita a sobrecarga dos operadores de igualdade em alguma classe.&lt;/p>&lt;/blockquote>
&lt;h2 id="diferença-entre-as-formas-de-comparação">Diferença entre as formas de comparação
&lt;/h2>&lt;p>Para analisar o IL gerado em ambos os casos, usei o código abaixo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="n">C1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">object&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="n">C2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">object&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="p">==&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>O resultado em ambos os casos foi o mesmo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">IL_0000: ldarg.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0001: ldnull
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0002: ceq
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0004: ret
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>💡 &lt;strong>Explicando (brevemente) as instruções&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>ldarg.1&lt;/code>: carrega o primeiro argumento do método para a pilha&lt;/li>
&lt;li>&lt;code>ldnull&lt;/code>: carrega o valor &lt;code>null&lt;/code> para a pilha&lt;/li>
&lt;li>&lt;code>ceq&lt;/code>: compara os valores da pilha e carrega 1 caso sejam iguais ou 0 caso contrário - esta instrução &lt;strong>é a comparação&lt;/strong> propriamente dita&lt;/li>
&lt;/ul>&lt;/blockquote>
&lt;p>Com isso é possível concluir que não há diferença entre as duas formas de comparação, pelo menos na maioria dos casos.&lt;/p>
&lt;h2 id="sobrecarregando-o-operador-de-igualdade">Sobrecarregando o operador de igualdade
&lt;/h2>&lt;p>Caso o operador de igualdade (&lt;code>==&lt;/code>) seja sobrecarregado em alguma classe para conter uma lógica própria, o cenário muda um pouco.&lt;/p>
&lt;p>Considerando a classe &lt;code>Numero&lt;/code> com o código abaixo&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Numero&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="n">Numero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">valor&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">Valor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">valor&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">Valor&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">get&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="kd">operator&lt;/span> &lt;span class="p">==(&lt;/span>&lt;span class="n">Numero&lt;/span> &lt;span class="n">n1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Numero&lt;/span> &lt;span class="n">n2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="kd">operator&lt;/span> &lt;span class="p">!=(&lt;/span>&lt;span class="n">Numero&lt;/span> &lt;span class="n">n1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Numero&lt;/span> &lt;span class="n">n2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>E os métodos de teste &lt;code>C3&lt;/code> e &lt;code>C4&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="n">C3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Numero&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="p">==&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="n">C4&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Numero&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Para o método &lt;code>C3&lt;/code> o IL gerado é &lt;strong>diferente do anterior&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">IL_0000: ldarg.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0001: ldnull
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0002: call bool Numero::op_Equality(class Numero, class Numero)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0007: ret
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>A linha &lt;code>IL_0002&lt;/code>, como esperado, ao invés de usar a instrução &lt;code>ceq&lt;/code> usa a sobrecarga definida na classe &lt;code>Numero&lt;/code>.&lt;/p>
&lt;p>Entretanto, no método &lt;code>C3&lt;/code> o código IL gerado é o mesmo dos exemplos anteriores.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">IL_0000: ldarg.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0001: ldnull
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0002: ceq
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IL_0004: ret
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="concluindo">Concluindo
&lt;/h2>&lt;p>O &lt;code>is null&lt;/code>, por bem ou por mal, não faz uso do operador sobrecarregado e, portanto, não está sujeito a possíveis erros de implementação ou mesmo uma implementação maliciosa. Embora estes casos sejam difíceis de serem encontrados no dia a dia, este é um efeito colateral interessante na minha opinião.&lt;/p>
&lt;p>No código da classe de exemplo o operador de igualdade contém um código problemático sempre retornando &lt;code>true&lt;/code>, desta forma, uma comparação do tipo &lt;code>== null&lt;/code> vai retornar &lt;code>true&lt;/code> mesmo para uma instância válida, enquanto o retorno da expressão &lt;code>is null&lt;/code> será false para instâncias válidas.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">var&lt;/span> &lt;span class="n">numero&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Numero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">numero&lt;/span> &lt;span class="p">==&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">numero&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Numero&lt;/span> &lt;span class="n">n2&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n2&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;p>O código usado nos exemplos pode ser encontrado &lt;a class="link" href="https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBLANmgExAGoAfAAQCYBGAWAChyBmAAipYGEWGBvBlgW1bAIEXJyYAKAHIBXALYwoEFgEMAlCwC8APjXatLAHazcuANyDu9K8xYixnFDIVKVG7XtUtsAZ2OmFvyCDuIc1JIQwABWMGAYLAiauok&amp;#43;/iZmloLBAqGclJExcQlJnqlahhlB9DnWtpSUdYJ8NlaCAG6qUAGKytrGMADuLHK9EJLU6ua1ttQAnJImYwYBZlMsAPQbLBhQsjAzguTzi659fqu461ssAGaquL4HrW1Nr6Nuxo2VgZaHAscFkZGhcqtdtrt9rUAL4MWE1RisdgfZS8ACQtTsKPG2CMCU6uGgyT0ADUHtB&amp;#43;gToNMEbZWLiEmTCd0eCwAOYwDCWeGYpHUJD2UTiCAAByUqgwFIqLmWRmoaBGZxUwOJOz2MBpdLY/MFjlF4sl3QAhFoZZ85QrsV9VZCNRj6PCgA" target="_blank" rel="noopener"
>no SharpLab&lt;/a>.&lt;/p></description></item></channel></rss>