Are nullable reference types not Nullable<T>? - c#

I created a new C#10/.net6 library project in VS2022 using the standard template, NRTs are enabled by default (nullable : enabled). I notice that nullable reference types do not appear to support Nullable properties Value and HasValue
MRE (using #nullable annotation explicitly)
using System;
public class Program
{
public static void Main()
{
#nullable enable
object? o =null;
if(o.HasValue)
{}
Console.WriteLine("Hello World");
}
}
'object' does not contain a definition for 'HasValue' and no
accessible extension method 'HasValue' accepting a first argument of
type 'object' could be found
Are nullable reference types not actually Nullable<T>? o is declared as object? so why does the compiler refer to object rather than object??

No, they are not. From the docs:
However, nullable reference types and nullable value types are implemented differently: nullable value types are implemented using System.Nullable<T>, and nullable reference types are implemented by attributes read by the compiler. For example, string? and string are both represented by the same type: System.String. However, int? and int are represented by System.Nullable<System.Int32> and System.Int32, respectively.
Basically NRT is just compile time information (though you can get access it in runtime via reflection like done here).
As for the case - just use ordinary null check and that's it.
if(o != null)
{
}
or pattern matching:
if(o is {} notNullO)
{
}

Related

Nullable generic parameter C# [duplicate]

Let's say we have some generic method with 2 parameters:
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
where TKey : notnull { }
That's perfectly fine from .NET6 perspective, where not nullable reference types were added. But using attempt is failing with reason CS8714 type can't be used as parameter:
private void TestMethod()
{
var id = 5;
int? nullableId = null;
TestConstraints(id, nullableId);
}
Warning CS8714 The type 'int?' cannot be used as type parameter 'TKey' in the generic type or method 'TestClass.TestConstraints(TKey, TKey?)'. Nullability of type argument 'int?' doesn't match 'notnull' constraint.
Specifying type explicitly as int does not help.
Could someone clarify if I could operate this way (defining input parameters as TKey?) or not, and where in documentation it is stated?
Compiler version:
Microsoft (R) Build Engine version 17.2.0+41abc5629
For unconstrained (neither to struct nor to class) generic type parameter T - T? is handled differently for value types and reference types. From the docs:
If the type argument for T is a reference type, T? references the corresponding nullable reference type. For example, if T is a string, then T? is a string?.
If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.
If the type argument for T is a nullable reference type, T? references that same nullable reference type. For example, if T is a string?, then T? is also a string?.
If the type argument for T is a nullable value type, T? references that same nullable value type. For example, if T is a int?, then T? is also a int?.
This happens partially due to the differences in how nullable reference and nullable value types are handled, cause nullable value types are actually separate types - Nullable<T>.
For TestConstraints let's imagine that T is int then according to the rules the signature becomes TestConstraints<int>(int key, int nullableKey) which obviously will not compile for the TestConstraints<int>(id, nullableId) call due to the type mismatch (TestConstraints<int>(id, nullableId.Value) will compile but throw at the runtime).
For int? (Nullable<int>) the signature becomes TestConstraints<int?>(int? key, int? nullableKey) which will compile (due to the implicit conversion T -> Nullable<T>) but obviously will fail the generic constraint with the warning.
The workaround can be to introduce two overloads, one for struct, one for class and let the compiler figure it out:
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
where TKey : struct
{ }
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
where TKey : class
{ }

How to avoid null params with nullable reference types

Since C# 8.0 nullable reference types are possible. If they are enabled you have to append the ? to the reference type to make it nullable.
My question now is how can I force a method parameter that uses the params keyword, to be not-nullable.
Example:
#nullable enable
public void MyMethod(params int[] foo)
{
// foo can still be null
// How can I ensure that foo is not null at compile-time
}
MyMethod(null); // valid, but why?
Another question which is similar, would be: Why is params int[] foo even nullable in the first place? I would expect something like params int[]? foo to make it nullable. But with params this seems to be different.
I tried NotNullAttribute but this only gives a warning. I want to disallow that completely by the compiler.
Does anybody know a way to make it non-nullable and maybe can tell me why params allows nullable even with #nullable enable?
valid, but why?
Parameter arrays where introduced before the nullable reference types and from the documentation it is valid to pass null as a value for such parameter:
A caller can then invoke the method in either of four ways:
By passing an array of the appropriate type that contains the desired number of elements.
By passing a comma-separated list of individual arguments of the appropriate type to the method.
By passing null.
By not providing an argument to the parameter array.
Does anybody know a way to make it non-nullable and maybe can tell me why params allows nullable even with #nullable enable?
If the call is made inside nullable enabled context compiler
should issue a warning. You can change this warning into by changing compiler settings. See this question.
this will be valid too
MyMethod();
it has nothing to do with nullables. You use nullable a wrong way here. It is because you are using the params keyword.
From MSDN
"Using the params keyword, you can specify a method parameter that takes a variable number of arguments. The parameter type must be a single-dimensional array.
When you call a method with a params parameter, you can pass in:
....
No arguments. If you send no arguments, the length of the params list is zero. "
When you pass null, you are assigning null to arguments explicitly.
but if you want to use nullable and compiler wornings, make your code like this
#nullable enable
public void MyAnotherMethod()
{
MyMethod(null); // Compiller will issue a warning
MyMethod(); // No warning
}
Referencing to the MSDN concerning Nullable reference types
Known pitfalls
Arrays and structs that contain reference types are known pitfalls in nullable references and the static analysis that
determines null safety. In both situations, a non-nullable reference
may be initialized to null, without generating warnings.
So you can't make these types non-nullable because params is based on array.
Possible solution is to create your own non-nullable type or pass existing (which implements IEnumerable, for example). I think you should reach the same effect as with params.

Is the C# compiler optimizing nullable types?

Can anybody shed any light on why this unit test is failing in Visual Studio 2013?
[TestMethod]
public void Inconceivable()
{
int? x = 0;
Assert.AreEqual(typeof(int?), x.GetType());
}
Your test is failing because:
Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.
You can read more from How to: Identify a Nullable Type.
Some examples taken from the previous article:
int? i = 5;
Type t = i.GetType();
Console.WriteLine(t.FullName); //"System.Int32"
Also note that:
The C# is operator also operates on a Nullable's underlying type. Therefore you cannot use is to determine whether a variable is a Nullable type. The following example shows that the is operator treats a Nullable<int> variable as an int.
int? i = 5;
if (i is int) { ... } // true
You are correct in presuming that the C# compiler is optimizing nullable types. Here's a quote from Jon Skeet's C# in Depth which should answer your question:
It’s only with respect to boxing and unboxing that the CLR has
any special behavior regarding nullable types. In fact, the behavior was only changed shortly before the release of .NET 2.0, as the result of community requests.
An instance of Nullable is boxed to either a null reference (if it doesn’t have a value) or a boxed value of T (if it does). It never boxes to a “boxed nullable int”—there’s no such type.
There's a similar thread on StackOverflow: Nullable type is not a nullable type?

What C# data types can be nullable types?

Can someone give me a list, or point me to where I can find a list of C# data types that can be a nullable type?
For example:
I know that Nullable<int> is ok
I know that Nullable<byte[]> is not.
I'd like to know which types are nullable and which are not. BTW, I know I can test for this at runtime. However, this is for a code generator we're writing, so I don't have an actual type. I just know that a column is string or int32 (etc).
All value types (except Nullable<T> itself) can be used in nullable types – i.e. all types that derive from System.ValueType (that also includes enums!).
The reason for this is that Nullable is declared something like this:
struct Nullable<T> where T : struct, new() { … }
A type is said to be nullable if it can be assigned a value or can be assigned null, which means the type has no value whatsoever. Consequently, a nullable type can express a value, or that no value exists. For example, a reference type such as String is nullable, whereas a value type such as Int32 is not. A value type cannot be nullable because it has enough capacity to express only the values appropriate for that type; it does not have the additional capacity required to express a value of null.
The Nullable structure supports using only a value type as a nullable type because reference types are nullable by design.
The Nullable class provides complementary support for the Nullable structure. The Nullable class supports obtaining the underlying type of a nullable type, and comparison and equality operations on pairs of nullable types whose underlying value type does not support generic comparison and equality operations.
From Help Docs
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
It can be any value type including struct, it cannot be a reference type, as those are inherently nullable already.
Yes:
Int32
double
DateTime
CustomStruct
etc.
No:
string
Array
CustomClass
etc.
For more information, see MSDN: http://msdn.microsoft.com/en-us/library/2cf62fcy(v=VS.80).aspx
Any data type can be nullable.
Nullable works for you because normal int is not nullable, so the conversion to nullable int is necessary.
Nullable<int[]> not works because int[] is already a nullable type.
Nullable does not support types that are already nullable.
Note, that you need to convert a type to be nullable only if he is not already a nullable type.
Value types are not nullable, value types are int, float, double, long etc, as well as structs you create.
While reference types, for example, are already nullable (classes for example).
You can use the ? operator to make the type to be a nullable type.
Note, that even tho it's not a good practice if you would use the question mark operator on a type that is already nullable it will not raise an error.
int? myNullableVar = null;
int[]? MyNullableArr = null; //not necessary but does not raise an error

Do C# Nullable variables still function as value types?

If I declare a nullable (either via Nullable or the ? symbol) variable from a value type, does it still respect the rules of value types (i.e. pass by value by default, deallocated when it falls out of scope, not when the garbage collector runs) or does it turn into a reference type since it's actually of type Nullable?
The documentation is here:
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
As you can see, the documentation describes this as the "nullable structure", indicating that it is a value type. Also, the documentation gives the first lines of the declaration of the type:
public struct Nullable<T> where T : struct, new()
again showing that the type is a value type.
Yes, System.Nullable is a generic Struct, ie value type.
Nullable value types are essentially just a struct wrapper around a value type which allows them to be flagged as null.
something like this:
struct Nullable<T>
{
public T Value;
public bool HasValue;
}
I believe it's actually more complex than that under the hood, and the compiler does lots of nice stuff for you so you can write for example if(myInt == null). But yes, they are still value types.

Categories