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:
CS1003: Syntax error, ';',
CS1525: Invalid expression term ')',
CS0103: The name 'i' does not exist in the current context.
'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?
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.
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:
CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead (Could not find documentation about CS8116 to reference)
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.
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;
}
Related
I can't seem to wrap my head around the compiler's warning in this case:
using System;
using System.Collections.Generic;
#nullable enable
public class Program
{
public static void Main()
{
Guid guid = Guid.NewGuid();
Dictionary<Guid, string> d = new();
bool found = d.TryGetValue(guid, out string? str);
if (found is false)
{
return;
}
string s = str; // WARNING: possible null value
}
}
After all, I'm doing the found check and return if there is no value (e.g. when the out str value is null). Plus, the out parameter of the .TryGetValue method is annotated with [MaybeNullWhen(false)].
Would appreciate your help figuring our where I'm wrong in my expectations and fixing the code, thanks. Code is here.
Basically the compiler (or language specification) isn't "smart" enough to carry the conditional processing of the return value from TryGetValue over when you use a local variable.
If you inline the TryGetValue call into the if condition, it's fine:
if (!d.TryGetValue(guid, out string? str))
{
return;
}
string s = str; // No warning
It's possible that over time this will evolve to be more sophisticated, but it's relatively difficult to specify this sort of thing in a bullet-proof way.
This isn't limited to nullable reference types - there are other cases where logically code is fine from a human perspective, but the compiler will reject it. For example:
string text;
bool condition = DateTime.UtcNow.Hour == 5;
if (condition)
{
text = "hello";
}
if (condition)
{
Console.WriteLine(text); // Error: use of unassigned local variable
}
We know that if we get into the second if statement body, we'll also have gone into the first one so text will have been assigned a value, but the rules of the compiler don't attempt to be smart enough to spot that.
I am playing with the nullable types in c# 8 and I found a problem that is bugging me.
Suppose I have a method which takes a nullable parameter. When a parameter is null, I want to throw a specific Exception. But I want the method to be clean and check the parameter somewhere else. The check method throws an exception, so after the method the parameter can not be null.
Unfortunately, the compiler does not see that and throws warnings at me.
Here's the method:
public void Foo(string? argument)
{
GuardAgainst.Null(argument, nameof(argument));
string variable = argument; // <-- Warning CS8600 Converting null literal or possible null value to non - nullable type
var length = argument.Length; //<--Warning CS8602 Dereference of a possibly null reference
}
Here's the check method:
public static void Null(string? text, string paramName)
{
if (text == null)
throw new ArgumentNullException(paramName);
}
Now, I can suppress the warning like this:
#pragma warning disable CS8602
var length = argument.Length;
#pragma warning restore CS8602
but it kind of kills my intention to keep my code clean.
So my question is: is there a nicer way to suppress the warnings? Or maybe tell a compiler that from now on the parameter is guaranteed to not be null?
This does what you want:
public static void Null<T>([NotNull] T? value, string paramName)
{
if (value == null)
throw new ArgumentNullException(paramName);
}
The [NotNull] attribute instructs the analysis that, after calling this method, value will not be null.
This means you don't need the ! operator, which is much cleaner and more natural.
void M(string? argument)
{
GuardAgainst.Null(argument, nameof(argument));
string variable = argument; // no warning
// ...
}
The use of an unconstrained generic type parameter T here means that this approach works for both reference types (such as string) and nullable value types (such as int?).
If you're using .NET 6, you can simplify this even further via CallerArgumentExpressionAttribute as follows:
public static void Null<T>(
[NotNull] T? value,
[CallerArgumentExpression(parameterName: "value")] string? paramName = null)
{
if (value == null)
throw new ArgumentNullException(paramName);
}
With that, the second argument can be omitted, and the caller can be simplified to:
GuardAgainst.Null(argument);
Think of the ? specifier on a type as meaning two things: 1) the value can be null before the call, and 2) the value can be null afterwards. Another way of writing it is [AllowNull, MaybeNull]. The absence of ? in a nullable context equally means [DisallowNull, NotNull]. In the case of your Null method, we end up with [AllowNull, NotNull] due to the manual specification of NotNull.
Ok, it looks like there is a really simple solution to it - the ! operator
You have to use it once after the guard, and then it considered to be not null:
public void Foo(string? argument)
{
GuardAgainst.Null(argument, nameof(argument));
var length = argument!.Length;
}
Consider this solution with the null-coalescing operator ??
The null-coalescing operator ?? returns the value of its left-hand
operand if it isn't null; otherwise, it evaluates the right-hand
operand and returns its result. The ?? operator doesn't evaluate its
right-hand operand if the left-hand operand evaluates to non-null.
public void Foo(string? argument)
{
string variable = argument ?? throw new ArgumentNullException(nameof(argument));
var length = argument.Length;
}
This solution is much cleaner in my opinion. You avoid inspecting GuardAgainst class and .Null() static method implemetation details.
I am using C# 8.0 with NullableContextOptions (Nullable Reference) enabled.
I have a function with this signature:
public static (int line, string content)? GetNextNonEmptyLineDown(this IList<string> contents, int fromLine)
Basically, it returns the line and the content if there is non-empty line down, and returns if there is none.
The problem is I don't know how to deconstruct it. If I try:
var (firstLineIndex, firstLine) = content.GetNextNonEmptyLineDown(0);
I receive the 4 syntax errors:
So I can only use:
var lineInfo = content.GetNextNonEmptyLineDown(0);
var firstLineIndex = lineInfo.Value.line;
var firstLine = lineInfo.Value.content;
which destroys the purpose. lineInfo is of type struct<T> where T is (int line, string content)
Is there anyway to deconstruct a nullable tuple?
EDIT: after posting the question, it comes to my mind that it makes no sense to allow deconstructing of nullable tuple as the data type of the variables may not be determined. Here is my current fix but wonder if this is the best way:
var lineInfo = content.GetNextNonEmptyLineDown(0);
if (lineInfo == null)
{
throw new Exception();
}
var (firstLineIndex, firstLine) = lineInfo.Value;
From this question on nullable types:
Any Nullable is implicitly convertible to its T, PROVIDED that the entire expression being evaluated can never result in a null assignment to a ValueType.
So what you're looking for is to place at the right of the deconstruction a surely not-null expression. An elegant way to do this is:
var (firstLineIndex, firstLine) = lineinfo ?? default;
?? is the null-coalescing operator: it returns the value at its left if it is not null, the one at its right otherwise.
What we place at the right is the default operator, which nicely fits its return type to the surrounding expression.
Note that the use of default is mainly to please the compiler, you may not want that value to be actually used at runtime. You should still check for null beforehand.
If you want to deconstruct nullable tuple of value types into Nullable this works(DateTime example):
(DateTime start, DateTime end)? foo = null;
(DateTime? start, DateTime? end) = foo ?? ((DateTime?)null, (DateTime?)null);
Ugly as sin, but works... Could be prettied up with extension methods, but you'd pretty much need one for each arity of ValueTuple.
I've noticed that some .NET structs can be compared to null.
For example:
TimeSpan y = new TimeSpan();
if (y == null)
return;
will compile just fine (the same with the Guid struct).
Now I know that stucts are value type and that the code above should not compile, unless there's an overload of operator == which takes an object. But, as far as I could tell there isn't.
I've looked at the class with Reflector, and also at the docs on MSDN.
The two of them do implement the following interfaces:
IComparable, IComparable<T>, IEquatable<T>
but, trying to implment the same Interfaces did not seem to help:
struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
public int CompareTo(Object obj) {
return 0;
}
public int CompareTo (XX other){
return 0;
}
public bool Equals (XX other){
return false;
}
public override bool Equals(object value){
return false;
}
public static int Compare(XX t1, XX t2){
return 0;
}
}
I'm using: .NET 2.0 Visual Studio 2005.
Does anyone has any idea what's the reason for this ?
I am just trying to get a better understanding. This isn't an issue as I know I shouldn't compare structs to null anyway.
It's the == operator.
The TimeSpan class has an overload of the equality operator:
public static bool operator ==(DateTime d1, DateTime d2)
{
return (t1._ticks == t2._ticks);
}
This in itself doesn't make it possible to compare with null, but...
With the arrival of nullable types, each struct is implicitly convertible to its nullable type, so when you see something like
TimeSpan y = new TimeSpan();
if (y == null)
return;
You don't see that this is happening:
TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
return;
Null gets the implicit conversion (implicit assignment?), but not all System.Object objects do:
TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
return;
Okay, but the equality operator doesn't take nullable arguments, does it?
Well, msdn is of help here, stating:
The predefined unary and binary
operators and any user-defined
operators that exist for value types
may also be used by nullable types.
These operators produce a null value
if [any of] the operands are null; otherwise,
the operator uses the contained value
to calculate the result.
So you effectively get a nullable implementation for each operator for free, with a fixed defined behaviour. The "contained value" mentioned above is the actual value the non-nullable operator would return.
This problem was effectively introduced when nullable types were included. There's an implicit conversion from TimeSpan to TimeSpan?, and there's a comparison between TimeSpan? and the null value of that type.
The compiler issues a warning for some types which makes it clearer what it's trying to do:
int x = 10;
if (x == null)
{
Console.WriteLine();
}
Gives this warning:
Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
since a value of type 'int' is never equal to 'null' of type 'int?'
I believe Marc Gravell and I worked out the circumstances under which the warning is given once... it's a shame it's not consistent.
This case is covered for generics in section 7.9.6 of the C# language specification.
The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.
I dug through the spec for a bit and couldn't find a more general rule. Jon's answer indicates it's a nullable promotion issue.
This rule (or a similar variation) does seem to be being applied here. If you look at the reflected output closely you'll notice the comparison isn't there. The C# compiler is apparently optimizing this comparison away and replacing it with false.
For instance, if you type the following
var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);
Then decompile it you'll see the following
var x = new TimeSpan();
var y = false;
Console.WriteLine(x);
See also: C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)
Starting with the C# 3 compiler that means it sometimes doesn't even warn you about this ;-p
Because Guid / TimeSpan etc provide ==, they fall into this trap where it doesn't warn you.
I FOUND IT :)
The following gives a warning:
int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always 'false' since a value of
// type 'int' is never equal to 'null' of type 'int?'
The compiler is just failing to emit the correct warning that the null you typed was converted to type TimeSpan? for the comparison.
Edit: The related section in the spec is §13.7.1 stating that null can be implicitly converted to any nullable type, and (the very difficult to read) section §13.7.2 stating a value type T can be implicitly converted to T?.
What I originally wrote:
Whatever's happening is something in the C# spec because like JaredPar says it compiles to simply false.
Note that this doesn't compile:
TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
...
C# accepts the following:
object o = "hello";
int? i = o as int?;
if (i == null) {
// o was not a boxed int
}
else {
// Can use i.Value to recover the original boxed value
}
But not
String o = "hello";
int? i = o as int?;
if (i == null) {
// o was not a boxed int
}
else {
// Can use i.Value to recover the original boxed value
}
I'm just wondering about the behaviour of the keyword as in C#.
The same as in Java this would fail:
Object test1 = "hello";
Integer test2 = (Integer) test1;
String test3 = "hello";
Integer test4 = (Integer) test3; //compilation error
The compiler knows that a string can never be an int? so it tells you that. That doesn't mean that int? isn't useful. Your attempted use case is far from the normal one. The normal one is "I want to represent an integer and the possibility that the value is missing/unknown". For that, int? works extremely well.
Why would you expect your original code to work? Why would it be helpful?
Note that you can use as with nullable types, for unboxing:
object o = "hello";
int? i = o as int?;
if (i == null)
{
// o was not a boxed int
}
else
{
// Can use i.Value to recover the original boxed value
}
EDIT: Having seen your comment, you don't use as to parse things. You probably want to use int.TryParse:
string text = "123":
int value;
if (int.TryParse(text, out value))
{
Console.WriteLine("Parsed successfully: {0}", value);
}
else
{
Console.WriteLine("Unable to parse text as an integer");
}
If you're sure the string is meant to be an integer (i.e. it's a bug otherwise) then you can just use int.Parse:
int value = int.Parse(text);
That will throw an exception if the parsing fails.
Note also that both of these methods allows you to specify a format provider (usually a culture info) which allows you to express how numbers are expressed in that format (e.g. thousands separators).
EDIT: In answer to your new question, the compiler prevents this because it knows a string can't possibly be a boxed int - the conversion will never ever succeed. When it only knows that the original value is an object, it might succeed.
For instance, suppose I said to you, "Here's a shape: is it a square?" That's a sensible question. It's reasonable to ask it: you can't tell without looking at the shape.
If, however, I said: "Here's a triangle: is it a square?" Then you'd be reasonably entitled to laugh in my face, as a triangle can't possibly be a square - the question doesn't make sense.
int? means a nullable integer type, not an int that could contain any other type of variable.
If you want a variable type that could contain an int or a string, you'd have to use an object, or a string I suppose, and then live a life filled with type casting. I don't know why you would want to do that, though.
int? allows you to store any integer value, or a null value. Which is useful when say the answer to the question "How many orders has this person placed" is legitimately "I don't know" instead of a number of orders, or zero which would be "I know for a fact this person has never placed an order".
I want to add some further information.
An other case, why the cast is invalid and the compiler throws an error on compilation is, that System.String is marked as sealed. So the compiler knows from which types System.String inherites and to which types you can cast the string using the as-operator.
Due to the keyword sealed, the compiler also knows that you cannot inherit from System.String to add functionality or implement some additional interfaces.
The code below is an example and the compiler will throw the following error on compilation
Cannot convert type 'SealedClass' to
'ICastToInterface' via a reference
conversion, boxing conversion,
unboxing conversion, wrapping
conversion, or null type conversion
public class UnsealedClass {
// some code
}
public sealed class SealedClass {
// some code
}
public interface ICastToInterface {
// some code
}
public class Test {
public Test() {
UnsealedClass unsealedClass = new UnsealedClass();
SealedClass sealedClass = new SealedClass();
ICastToInterface unsealedCast = unsealedClass as ICastToInterface; // This works fine
ICastToInterface sealedCast = sealedClass as ICastToInterface; // This won´t compile, cause SealedClass is sealed
}
}
but you can check the value of null and set it to null.
int? blah;
if (blah == null)
{}
int? is a nullable integer, it has nothing to do with casting and the as keyword. "String" is a string type object, which is not convertible to an int (nullable or non-nullable).
The as Keyword is virtually the same as casting using brackets except it will not return an error, it will set the object to null:
int i = 1;
object o = i; //boxing
int j = (int)o; //unboxing
This first example works as the object assigned to o is an int.
Now consider:
string i = "MyString";
object o = MyString;
int j = (int)o //unboxing raises exception
int j = o as int; //raises compilation Error as int is not nullable
int? j = o as int?; /// o == null
I hope that that helps to explain the difference between the two concepts.
Richard