Why is Nullable<T> nullable? Why it cannot be reproduced? - c#

When I write
Nullable<Nullable<DateTime>> test = null;
I get a compilation error:
The type 'System.Datetime?' must be a non-nullable value type in order to use it as a paramreter 'T' in the generic type or method 'System.Nullable<T>'
But Nullable<T> is a struct so it's supposed to be non-nullable.
So I tried to create this struct:
public struct Foo<T> where T : struct
{
private T value;
public Foo(T value)
{
this.value = value;
}
public static explicit operator Foo<T>(T? value)
{
return new Foo<T>(value.Value);
}
public static implicit operator T?(Foo<T> value)
{
return new Nullable<T>(value.value);
}
}
Now when I write
Nullable<Foo<DateTime>> test1 = null;
Foo<Nullable<DateTime>> test2 = null;
Foo<DateTime> test3 = null;
The first line is ok but for the second and third lines I get the two following compilation error:
The type 'System.DateTime?' must be a non-nullable value type in order to use it as a parameter 'T' in the generic type or method 'MyProject.Foo<T>' (second line only)
and
Cannot convert null to 'MyProject.Foo<System.DateTime?> because it is a non-nullable value type'
Foo<Nullable<DateTime>> test = new Foo<DateTime?>();
doesn't work neither event if Nullable<DateTime> is a struct.
Conceptually, I can understand why Nullable<T> is nullable, it avoids having stuffs like DateTime?????????? however I can still have List<List<List<List<List<DateTime>>>>>...
So why this limitation and why can't I reproduce this behavior in Foo<T>? Is this limitation enforced by the compiler or is it intrinsic in Nullable<T> code?
I read this question but it just says that it is not possible none of the answers say fundamentally why it's not possible.

But Nullable is a struct so it's supposed to be non-nullable.
Nullable<T> is indeed a struct, but the precise meaning of the generic struct constraint as stated in the docs is:
The type argument must be a value type. Any value type except Nullable can be specified. See Using Nullable Types (C# Programming Guide) for more information.
For the same reason, your line
Foo<Nullable<DateTime>> test2 = null;
results in the compiler error you are seeing, because your generic struct constraint restricts your generic T argument in a way so Nullable<DateTime> must not be specified as an actual argument.
A rationale for this may have been to make calls such as
Nullable<Nullable<DateTime>> test = null;
less ambiguous: Does that mean you want to set test.HasValue to false, or do you actually want to set test.HasValue to true and test.Value.HasValue to false? With the given restriction to non-nullable type arguments, this confusion does not occur.
Lastly, the null assignment works with Nullable<T> because - as implied by the selected answers and their comments to this SO question and this SO question - the Nullable<T> type is supported by some compiler magic.

The error is saying that the type-parameter of Nullable should be not-nullable.
What you're doing, is creating a Nullable type which has a nullable type-parameter, which is not allowed:
Nullable<Nullable<DateTime>>
is the same as
Nullable<DateTime?>
Which is quite pointless. Why do you want to have a nullable type for a type that is already nullable ?
Nullable is just a type that has been introduced in .NET 2.0 so that you are able to use ' nullable value types'. For instance, if you have a method wich has a datetime-parameter that is optional; instead of passing a 'magic value' like DateTime.MinValue, you can now pass null to that method if you do not want to use that parameter.

In generic classes where T: struct means that type T cannot be null.
However Nullable types are designed to add nullability to structs. Technically they are structs, but they behave like they may contain null value. Because of this ambiguity the use of nullables is not allowed with where T: struct constraint - see Constraints on Type Parameters
Nullable types are not just generic structs with special C# compiler support. Nullable types are supported by CLR itself (see CLR via C# by Jeffrey Richter), and looks like this special CLR support makes them non-recursive.
CLR supports special boxing/unboxing rules int? i = 1; object o = i will put int value into variable o and not Nullable<int> value. In case of multiple nullables - should o = (int??)1; contain int or int? value?
CLR has special support for calling GetType and interface members - it calls methods of underlying type. This actually leads to situation when Nullable.GetType() throws a NullObjectReference exception, when it has NullValueFlag.
As for C#, there are a lot of features in C# that are hard-coded for nullable types.
Based on this article Nullable Types (C# Programming Guide) the primary goal of introducing nullable types is to add null support for the types that do not support nulls. Logically, since DateTime? already supports nulls it shouldn't be allowed to be "more" nullable.
This document also plainly states that
Nested nullable types are not allowed. The following line will not compile: Nullable<Nullable<int>> n;
Special C# features of nullable types:
C# has special ?? operator. Should (int???)null ?? (int)1 resolve to (int??)1 or to (int)1 value?
Nullables have special System.Nullable.GetValueOrDefault property. What should it return for nested nullables?
Special processing for ? == null and ? != null operators. If the Nullable<Nullable<T>> contains Nullable<T> value, but this value is null, what should HasValue property return? What should be the result of comparison with null?
Special implicit conversions. Should int?? i = 10 be implicitly convertible?
Explicit conversions. Should be int i = (int??)10; supported?
Special support for bool? type Using nullable types. E.g. (bool?)null | (bool?)true == true.
So, should CLR support recursive GetType() call? Should it remove Nullable wrappers when boxing value? If it should do for one-level values, why don't for all other levels as well? Too many options to consider, too many recursive processing.
The easiest solution is to make Nullable<Nullable<T>> non-compilable.

Related

C# 8 gives a warning when returning a nullable generic with nullable constraint

This code:
public T Foo<T>()
where T : class?
{
return null;
}
Gives a following error:
A null literal introduces a null value when 'T' is a non-nullable
reference type
I don't see why we can't return null when we say that T can be nullable. If we additionally try to return T? we will get an error that T has to be non-nullable.
It seems it's kind of impossible to have a nullable constraint and return a nullable result at the same time.
Imagine you call:
string result = Foo<string>();
result now contains null. But it's a string, which is not nullable.
The compiler is warning you that Foo<T> may be called where T is not nullable, and returning null in this case would be unexpected.
Note that where T : class? means that T may be nullable, but it also might not be. Both string and string? are allowed. I don't believe there's any way to say "T must be nullable".
If you're trying to say:
T is allowed to be nullable
When T is non-nullable, then this type can still return null
Then you can write:
[return: MaybeNull]
public T Foo<T>()
where T : class?
{
return null!;
}
SharpLab
Note that MaybeNull only applies to the method's contract and not its body, when is why we need to return null!. However you can see in the SharpLab link above that the caller string result = Foo<string>(); gets the correct warning.
Let's start from basic. Let say you have a reference type variable that allows it to have null value. But your program design requires it to be not null all times, if a null value is encountered, it is mostly likely to get the NullReferenceException.
To avoid such design issues, The NullableReference types was introduced. Means if a reference type is allowed to have null you should mark it as Nullable and any usage should check for null value before using it. If checking is not found then compiler will generate a warning.
If you read this introductory article on NullableReferenceTypes You will get the fair idea of what is the intent of this new feature.

Overloading the = operator for nullable types?

The MSDN mentions that overloading the = operator is not possible.
How is it possible then for Nullable types to be assigned to null?
int? i = null;
Besides can I do it with my own generic types and how?
It's the implicit-conversion not the assignment-operator that allows to assign null: http://msdn.microsoft.com/en-us/library/ms131346(v=vs.110).aspx
If the value parameter is not null, the Value property of the new
Nullable<T> value is initialized to the value parameter and the
HasValue property is initialized to true. If the value parameter is
null, the Value property of the new Nullable<T> value is initialized
to the default value, which is the value that is all binary zeroes,
and the HasValue property is initialized to false.
Essentially what Tim's comment (Edit: And now answer =D) says - There's an implicit conversion from the null literal, rather than an overload of the assignment operator.
From the C# language spec (I was looking at Version 5.0) - Section "6.1.5 Null literal conversions":
An implicit conversion exists from the null literal to any nullable
type. This conversion produces the null value (§4.1.10) of the given
nullable type.
There is special compiler support for the Nullable type.
It is impossible to create a user-defined implicit conversion to/from null. They built it into the language (and the runtime) rather than creating Nullable on top of the language, as so many BCL classes are made.
Interestingly this is not the only special support created for Nullable. When you box a Nullable<T> it doesn't actually box a Nullable object, ever. If HasValue is false, null is boxed, and if it's true, the underlying value is unwrapped and boxed. It would be impossible to do this for your own type.
Nullable types are instances of the struct
System.Nullable<T>.
The type that can be specified or made nullable is specified as the generic type of nullable (T).
More info here...http://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx
In your example, you're not actually setting an int to null, rather setting the value on the struct which encapsulates it to null.

Why is 'struct Nullable<T>' not a struct?

Basically why is the following invalid in C#? I can find plenty of good uses for it and in fact can fix it by creating my own nullable struct class but why and how does the C# specification (and hence the compiler) prevent it?
The below is a partial example of what I'm talking about.
struct MyNullable<T> where T : struct
{
public T Value;
public bool HasValue;
// Need to overide equals, as well as provide static implicit/explit cast operators
}
class Program
{
static void Main(string[] args)
{
// Compiles fine and works as expected
MyNullable<Double> NullableDoubleTest;
NullableDoubleTest.Value = 63.0;
// Also compiles fine and works as expected
MyNullable<MyNullable<Double>> NullableNullableTest;
NullableNullableTest.Value.Value = 63.0;
// Fails to compile...despite Nullable being a struct
// Error: The type 'double?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'ConsoleApplication1.MyNullable<T>'
MyNullable<Nullable<Double>> MyNullableSuperStruct;
}
}
It is a struct. It just doesn't satisfy the value type generic type parameter constraint. From 10.1.5 of the language specification:
The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable type (§4.1.10) does not satisfy the value type constraint.
So, the where T : struct doesn't mean what you think it means.
Basically why is the following invalid in C#?
Because where T : struct can only be satisfied by T that are non-nullable value types. Nullable<TNonNullableValueType> does not satisfy this constraint.
why and how does the compiler prevent it?
Why? To be consistent with the specification. How? By performing syntactic and semantic analysis and determining that you've supplied a generic type parameter T that doesn't satisfy the generic type constraint where T : struct.
[I] can fix it by creating my own nullable struct class but
No, you're version doesn't fix it. It's basically exactly the same as Nullable<T> except you don't get special handling by the compiler, and you're going to cause some boxing that the compiler's implementation won't box.
I can find plenty of good uses for it
Really? Such as? Keep in mind, the basic idea of Nullable<T> is to have a storage location that can contain T or can represent "the value is missing." What's the point of nesting this? That is, what's the point of Nullable<Nullable<T>>? It doesn't even make conceptual sense. That might be why it's prohibited, but I'm merely speculating (Eric Lippert has confirmed that this speculation is correct). For example, what is an int??? It represents a storage location that represents the value is missing or is an int?, which is itself a storage location that represents the value is missing or is an int? What's the use?
One reason for the struct constraint's diallowing nullables is that we want to be able to use T? in generic methods. If struct permitted nullable value types, the compiler would have to prohibit T?.
The nullable type must have special handling in the compiler in other cases as well:
The null keyword must be implicitly convertible to a nullable type; this is impossible with a value type.
Nullable value types can be compared with the null keyword; with non-nullable value types, this comparison always returns false.
Nullable value types work with the ?? operator; non-nullables do not.

Why cant I declare a generic list as nullable?

Im trying to use the following code:
private Nullable<List<IpAddressRange>> ipAddressRangeToBind;
But I am getting the following warning:
The type List must be a non-nullable value type in
order to use it as a parameter 'T' in the generic type or method
'System.Nullable'.
List<T> is already a reference type (for any kind of T) - you can only declare Nullable<T> where T is a non-nullable value type (it's declared as Nullable<T> where T : struct).
But that's okay, because if you just declare:
private List<IpAddressRange> ipAddressRangeToBind;
then you can still have
ipAddressRangeToBind = null;
because reference types are always nullable.
List<IpAddressRange> is a reference type - it is already nullable - in fact it will be initialized to null by that declaration.
You can just use it as is:
List<IpAddressRange> ipAddressRangeToBind = null;
List is already nullable.
Reference types cannot be wrapped in Nullable<T> due to a where T : struct constraint on the generic.
The reasons for this constraint are:
Reference types are already nullable by definition, and
Nullable is not very space efficient, but more a "logical" nullability.
Nullable<T> has a bool property HasValue and a type T property Value which contains the actual value-type value.
Even if HasValue == false (that is, if the nullable wrapped variable is set to null), you STILL consume the space for the value type as if it was there.
It's logically nullable to allow you to specify optional behavior, but it doesn't save any space. This is very similar to how boost::optional works in C++.

Why can the as operator be used with Nullable<T>?

According to the documentation of the as operator, as "is used to perform certain types of conversions between compatible reference types". Since Nullable is actually a value type, I would expect as not to work with it. However, this code compiles and runs:
object o = 7;
int i = o as int? ?? -1;
Console.WriteLine(i); // output: 7
Is this correct behavior? Is the documentation for as wrong? Am I missing something?
Is this correct behavior?
Yes.
Is the documentation for as wrong?
Yes. I have informed the documentation manager. Thanks for bringing this to my attention, and apologies for the error. Obviously no one remembered to update this page when nullable types were added to the language in C# 2.0.
Am I missing something?
You might consider reading the actual C# specification rather than the MSDN documentation; it is more definitive.
I read:
Note that the as operator only performs reference conversions and boxing conversions. The as operator cannot perform other conversions, such as user-defined conversions, which should instead be performed by using cast expressions.
And boxing conversions.....
Just a guess, but I'd say it boxes o as an integer and then converts it to a nullable.
From the documentation about the as keyword:
It is equivalent to the following expression except that expression is evaluated only one time.
expression is type ? (type)expression : (type)null
The reference for is use also states it works with reference types, however, you can also do stuff like this:
int? temp = null;
if (temp is int?)
{
// Do something
}
I'm guessing it is just an inaccuracy in the reference documentation in that the type must be nullable (ie a nullable type or a reference type) instead of just a reference type.
Apparently the MSDN documentation on the as operator needs to be updated.
object o = 7;
int i = o as **int** ?? -1;
Console.WriteLine(i);
If you try the following code where we use the as operator with the value type int, you get the appropriate compiler error message that
The as operator must be used with a reference type or nullable type ('int' is a non-nullable value type)
There is an update though on the link in Community Content section that quotes:
The as operator must be used with a reference type or nullable type.
You're applying the 'as' to Object, which is a reference type. It could be null, in which case the CLR has special support for unboxing the reference 'null' to a nullable value type. This special unboxing is not supported for any other value type, so, even though Nullable is a value type, it does have certain special privledges.

Categories

Resources