Reputation: 9526
I like to use pattern-matching
on a nullable int
i.e. int?
:
int t = 42;
object tobj = t;
if (tobj is int? i)
{
System.Console.WriteLine($"It is a nullable int of value {i}");
}
However, this results in the following syntax errors:
'i)' is marked with a red squiggly line.
The expression compiles when using the old operator is
:
int t = 42;
object tobj = t;
if (tobj is int?)
{
System.Console.WriteLine($"It is a nullable int");
}
string t = "fourty two";
object tobj = t;
if (tobj is string s)
{
System.Console.WriteLine($@"It is a string of value ""{s}"".");
}
Also works as expected.
(I'm using c#-7.2 and tested with both .net-4.7.1 and .net-4.6.1)
I thought it had something to with operator precedence. Therefore, I have tried using parenthesis at several places but this didn't help.
Why does it give these syntax errors and how can I avoid them?
Upvotes: 41
Views: 9075
Reputation: 99
Now you can use the following syntax that works for classic object as weel as Nullable :
public static void Check(int? value)
{
if (value is {} v)
{
// Happy path
}
}
Upvotes: -3
Reputation: 101
For anyone wondering how to actually use pattern matching with nullables, you can do so with a generic helper function, like so:
public static bool TryConvert<T>(object input, out T output)
{
if (input is T result)
{
output = result;
return true;
}
output = default(T);
// Check if input is null and T is a nullable type.
return input == null && System.Nullable.GetUnderlyingType(typeof(T)) != null;
}
This will return true
if T
is a nullable or non-nullable of the same type that input
contains or if input
is null and T
is nullable. Basically works the same as normal, but also handles nullables.
Side note: Interestingly, from my testing, I found System.Nullable.GetUnderlyingType(typeof(T))
allocates 40 bytes of garbage every time it's called if T is nullable. Not sure why, seems like a bug to me, but that's potentially a hefty price to pay rather than just null-checking like normal.
Knowing that, here's a better function:
public static bool TryConvert<T>(object input, out T? output) where T : struct
{
if (input is T result)
{
output = result;
return true;
}
output = default(T?);
return input == null;
}
Upvotes: 3
Reputation: 43254
The type pattern in its various forms: x is T y
, case T y
etc, always fails to match when x
is null
. This is because null
doesn't have a type, so asking "is this null
of this type?" is a meaningless question.
Therefore t is int? i
or t is Nullable<int> i
makes no sense as a pattern: either t
is an int
, in which case t is int i
will match anyway, or it's null
, in which case no type pattern can result in a match.
And that is the reason why t is int? i
or t is Nullable<int> i
are not, and probably never will be, supported by the compiler.
The reason why you get additional errors from the compiler when using t is int? i
is due to the fact that, e.g. t is int? "it's an int" : "no int here"
is valid syntax, thus the compiler gets confused over your attempts to use ?
for a nullable type in this context.
As to how can you avoid them, the obvious (though probably not very helpful) answer is: don't use nullable types as the type in type patterns. A more useful answer would require you to explain why you are trying to do this.
Upvotes: 47
Reputation: 9526
Change your code into:
int t = 42;
object tobj = t;
if (tobj is Nullable<int> i)
{
Console.WriteLine($"It is a nullable int of value {i}");
}
This produces the more helpful:
Others (user @Blue0500 at github ) have tagged this behaviour as a bug Roslyn issue #20156. Reacting to Roslyn issue #20156, Julien Couvreur from Microsoft has said he thinks it is by design.
Neal Gafter from Microsoft working on Roslyn has also said better diagnostics are wanted for use of nullable type is switch pattern.
So, the error message can be avoided by using:
int t = 42;
object tobj = t;
if (tobj == null)
{
Console.WriteLine($"It is null");
}
else if (tobj is int i)
{
Console.WriteLine($"It is a int of value {i}");
}
Except for issues when parsing tobj is int? i
, this still leaves the question why is tobj is int? i
or tobj is Nullable<int> i
not allowed.
Upvotes: 6