This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.
Title
A subtle failure to pattern-match <c>null</c> in C#
Description
<img attachment="csharp-icon-clr.png" align="right">The article <a href="https://ayende.com/blog/202403-B/the-null-check-that-didnt-check-for-nulls" source="Ayende" author="Oren Eini">The null check that didn't check for nulls</a> points out an interesting and subtle difference in code-generation, depending on whether you use the <c>var</c> keyword. Using <c>var</c> in pattern-matching might lead to a pattern that <i>looks</i> like it checks for <c>null</c> but <i>doesn't</i>. You can see and play with a <a href="https://sharplab.io/#v2:D4AQDABCCMAsDcBYAUCmkAqBTAzgF2gAoAZAS3wB50A+CfAJxwEoUBvFCTiUgM0IZzdBAbQBuAQ3p0Aui2RcI7eQq4gA7HSTLOAXw6qN6AHQApAPakAdoQBEAGnt08jJlr2oP6CNnwAmEuR4VNBgtAJySgq8/M6C5BDCXjiy+pyRKpzqmqkQ7gpZxuZWtg52Ti5uKFWeId64eADMAZQ05cxsOdECQgmsOjJyCukZWThaCnkGUCGmFtb2juFuQA==" source="SharpLab.IO">live example</a> but I've replicated the examples below.
This is the problematic example:
<code>string Test1(List<string> strs)
{
if(strs is [var s])
{
return s;
}
return string.Join(",", strs);
}</code>
It's basically saying that the pattern should match anything that's a collection with one element. Since the type is obvious from the method signature's parameter <c>strs</c>, we use <c>var</c> instead of <c>string</c>. That generates the following code.
<code>internal static string $>g__Test1|0_0(List<string> strs)
{
if (strs != null && strs.Count == 1)
{
return strs[0];
}
return string.Join(",", strs);
}</code>
Note that it returns the first element <i>without checking it for <c>null</c></i>.
If you change the <c>var</c> to <c>string</c>, which, as noted above, is redundant, then the generated code includes a null-check.
<code>string Test2(List<string> strs)
{
if(strs is [string s])
{
return s;
}
return string.Join(",", strs);
}</code>
This is the generated code for the example above.
<code>internal static string $>g__Test2|0_1(List<string> strs)
{
if (strs != null && strs.Count == 1)
{
string text = strs[0];
if (text != null)
{
return text;
}
}
return string.Join(",", strs);
}</code>
If you instead use <c>{ }</c> to indicate that you want to match a non-null object, then you also get the null-check.
<code>string Test3(List<string> strs)
{
if(strs is [{} s])
{
return s;
}
return string.Join(",", strs);
}</code>
This is the generated code for the example above. It is the same as the second example that uses <c>string</c> for the matched parameter.
<code>internal static string $>g__Test3|0_2(List<string> strs)
{
if (strs != null && strs.Count == 1)
{
string text = strs[0];
if (text != null)
{
return text;
}
}
return string.Join(",", strs);
}</code>