Reputation: 3311
I have some code that effectively does this:
private void DoStuff(int? a)
{
int c = 0;
if (a is int b)
{
c = b;
}
}
But a is int b
gives me a warning:
Use not null pattern instead of a type check succeeding on any not-null value
Using Resharper's suggestion "Use null check pattern" autocorrects this code as follows, causing the warning message to disappear:
private void DoStuff(int? a)
{
int c = 0;
if (a is { } b)
{
c = b;
}
}
That's great and all but now I don't understand the code I'm writing. How should I interpret if(a is {} b)
in the english language?
Is it saying "if a
is not null set b
to a
's non-null value"?
Or is {}
a shorthand for "the underlying type of a
" (i.e. int
)?
Is there anything I can put inside the braces, or do the braces alone have their own meaning?
Anything to help me understand what this code really means would be appreciated. Thank you.
Upvotes: 21
Views: 8292
Reputation: 39358
Would that a
have been of type object
, then the if (a is int b)
check would be fine and I don't think Resharper would have complained. However, now a
has type int?
(or Nullable<int>
) so the check "is it really an int?" is the same as "is it not null?".
This can be expressed more clearly as
private void DoStuff(int? a)
{
int c = 0;
if (a.HasValue)
{
c = a.Value;
}
}
or shorter
private void DoStuff(int? a)
{
int c = a.GetValueOrDefault();
}
to assign the default value (0) when a
is null, or use the real value when not.
Upvotes: 0
Reputation: 6295
TL;DR resharper is incorrect, official language reference suggests if (a is int b)
. Anyway if (a is int b)
and if (a is { } b)
are compiled into exactly the same CIL.
The Resharper suggestion seems to be incorrect, even the official language reference for nullable value types (link) literally shows an example with if (a is int valueOfA)
:
int? a = 42;
if (a is int valueOfA)
{
Console.WriteLine($"a is {valueOfA}");
}
else
{
Console.WriteLine("a does not have a value");
}
// Output:
// a is 42
Even the official language reference for is
(link) operator shows:
int i = 34;
object iBoxed = i;
int? jNullable = 42;
if (iBoxed is int a && jNullable is int b)
{
Console.WriteLine(a + b); // output 76
}
Also if you inspect CIL, it's compiled in .NET 7 into the very same thing. See for yourself here.
if (a is { } b)
:
IL_0000: nop
IL_0001: ldc.i4.s 0
IL_0003: stloc.0
IL_0004: ldarga.s a
IL_0006: call instance bool valuetype [System.Runtime]System.Nullable`1<int32>::get_HasValue()
IL_000b: brfalse.s IL_0018
IL_000d: ldarga.s a
IL_000f: call instance !0 valuetype [System.Runtime]System.Nullable`1<int32>::GetValueOrDefault()
IL_0014: stloc.1
IL_0015: ldc.i4.1
IL_0016: br.s IL_0019
vs. if (a is int b)
:
IL_0000: nop
IL_0001: ldc.i4.s 0
IL_0003: stloc.0
IL_0004: ldarga.s a
IL_0006: call instance bool valuetype [System.Runtime]System.Nullable`1<int32>::get_HasValue()
IL_000b: brfalse.s IL_0018
IL_000d: ldarga.s a
IL_000f: call instance !0 valuetype [System.Runtime]System.Nullable`1<int32>::GetValueOrDefault()
IL_0014: stloc.1
IL_0015: ldc.i4.1
IL_0016: br.s IL_0019
Upvotes: 15
Reputation: 18865
The a is { }
is an example of using is
to match an expression against a pattern... reference
The is operator checks if the result of an expression is compatible with a given type. For information about the type-testing is operator, see the is operator section of the Type-testing and cast operators article.
Beginning with C# 7.0, you can also use the is operator to match an expression against a pattern, as the following example shows:
static bool IsFirstFridayOfOctober(DateTime date) =>
date is { Month: 10, Day: <=7, DayOfWeek: DayOfWeek.Friday }
The a is { } b
is an example of using a declaration pattern to declare a new local variable, scoped to the if
block. reference
You use declaration and type patterns to check if the run-time type of an expression is compatible with a given type. With a declaration pattern, you can also declare a new local variable. When a declaration pattern matches an expression, that variable is assigned a converted expression result
I highly recommend Sharplab.io to investigate the optimisations that take place during C# compilation. In your provided example:
private void DoStuff(int? a)
{
int c = 0;
if (a is { } b)
{
c = b;
}
}
Equates to:
private void DoStuff(Nullable<int> a)
{
int num = 0;
int valueOrDefault = default(int);
int num2;
if (a.HasValue)
{
valueOrDefault = a.GetValueOrDefault();
num2 = 1;
}
else
{
num2 = 0;
}
if (num2 != 0)
{
num = valueOrDefault;
}
}
So basically a is { } b
is saying; does a
match any pattern (i.e. not null) if yes, assign it to a local variable b
.
Personally I think that whatever code you write should be as readable as possible for everyone. If you are confused by the code you have written then don't write it, write something that the person taking over from you will understand.
Upvotes: 18