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.
Related
I have a method that should return a nullable reference type or a nullable value type:
private static T? FooValueOrReferenceType<T>(string input)
{
return null;
// the code above show this error
// Cannot convert null to type parameter 'T' because it could be
// a non-nullable value type. Consider using 'default(T)' instead.
}
I don't understand why I cant return null, since I have enabled nullable value types in the project
I know that adding constraint where T : struct, class can resolve the problem, but I want to understand why.
I noticed that this method signature:
private static T? FooValueType<T>(string input) where T : struct
compiled to:
private static Nullable<T> FooValueType<T>([System.Runtime.CompilerServices.Nullable(1)] string input) where T : struct
and this:
private static T? FooReferenceType<T>(string input) where T : class
compiles to:
private static T FooReferenceType<T>(string input) where T : class
What's the reason behind all this?
There is some question similar to this one, but the answers are not clear for a noob like me.
Ultimately, nullability means two fundamentally different things for reference-types and value-types. For value-types, T? means Nullable<T> - a wrapper layer around a T, with some compiler voodoo - and for reference-types, T? means simply T, but with some additional compiler context (and attributes added, to convey that intent). There is simply no way in IL of expressing these two things at the same time, and Nullable<T> and T cannot be used interchangeably in IL (even if C# makes it look like you can). Ultimately, "you just can't, IL doesn't work that way".
Your two experiments into the : class and : struct show these two incompatible variants nicely.
I am puzzled by the following behavior of Nullable types:
class TestClass {
public int? value = 0;
}
TestClass test = new TestClass();
Now, Nullable.GetUnderlyingType(test.value) returns the underlying Nullable type, which is int.
However, if I try to obtain the field type like this
FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
and I invoke
Nullable.GetUnderlyingType(field.FieldType).ToString()
it returns a System.Nullable[System.Int32] type. So that means the method Nullable.GetUnderlyingType() has a different behavior depending on how you obtain the member type. Why is that so? If I simply use test.value how can I tell that it's Nullable without using reflection?
smartcaveman's answer is the best one here so far in that it actually identifies the section of the documentation that describes this behaviour.
The behaviour is undesirable and unfortunate; it is due to the behaviour in combination of three features which, by themselves, behave reasonably.
The three features are:
GetType is a non-virtual method; it cannot be overridden. This should make sense; an object doesn't get to decide what its type is reported as. By making it non-virtual, the method is guaranteed to tell the truth.
The this value passed to a non-virtual method declared on object must be converted to object; therefore in the case of objects of value type, the receiver of the call to GetType() is boxed to object.
Nullable value types have no boxed form; when you box a nullable int, you either get a boxed int or you get a null reference. You never get a valid reference to a boxed nullable int.
Each feature is reasonable on its own but in combination the result is undesirable: when you call GetType on a valid nullable int, the runtime boxes the nullable int to a boxed int and then passes that as the this of object.GetType which of course reports int. If the value is a null nullable int, the runtime boxes to null and then invokes GetType on a null reference and crashes. Neither of these behaviours are desirable, but we're stuck with them.
Nullable types are a little bit weird. However, at least their behavior is well documented.
From the C# programming guide on MSDN,
"How to: Identify Nullable Types"at http://msdn.microsoft.com/en-us/library/ms366789(VS.80).aspx :
You can also use the classes and methods of the System.Reflection namespace to generate Type objects that represent Nullable types. However, if you attempt to obtain type information from Nullable variables at runtime using the GetType method or the is operator, the result is a Type object that represents the underlying type, not the Nullable type itself.
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.
It's worth pointing out that the distinction in your headline is inaccurate. The type behavior is not distinct based on local variables or properties, but on whether the type is accessed via a runtime object or by reflection (or use of the typeof operator). Your inference is understandable, since the types of local variables are usually only accessed by means of a runtime object, however, it is flawed, because if you access a nullable object at runtime via a property accessor then its behavior will be equivalent to that of a local variable.
Also, to answer the last part of your question explicitly: the only way to tell that test.value is nullable without using reflection would be to access it and get a NullReferenceException (which, of course, can only happen if test.value is null. As written, in your example, the value is not null, so determining this without reflection would be impossible
The problem is that you're assuming that the Type of test.value is the same as the Type of the field.FieldType of value, and it's not. Getting the Type of test.value actually gets the Type of what is stored in the field, in this case a 0.
Type t = test.value.GetType()
is the same (in your example) as
Type t = 0.GetType()
To demonstrate, initialize value to null and test.value.GetType() will throw a NullReferenceException
Method GetNulllableUnderlyingType returns Underlying type for nullable types, for another types returns null;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetNulllableUnderlyingType(new TestClass().value));
Console.WriteLine(GetNulllableUnderlyingType(new TestClass()));
Console.ReadKey();
}
public class TestClass
{
public int? value;
}
public static Type GetNulllableUnderlyingType<T>(T value)
{
Type result = Nullable.GetUnderlyingType(typeof (T));
return result;
}
}
First of all, Nullable.GetUnderlyingType(test.value) in your example will not compile. Or typeof(test.value) as I have seen in comments.
Lets answer this by the following modified code:
TestClass test = new TestClass();
Type type1 = test.value.GetType(); // Gets the type of the data inside the field ==> System.Int32.
Type underlyingType1 = Nullable.GetUnderlyingType(type1); // Gets the underlying type of System.Int32 ==> null.
FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
Type type2 = field.FieldType; // Now the type is retrieved of the field, not the data inside. ==> System.Int32?
Type underlyingType2 = Nullable.GetUnderlyingType(type2); // Gets the underlying type of System.Int32? ==> System.Int32.
When you do a GetType() on a field, you will get the type of the data inside, not the type of the field itself. For example:
object o = 1;
Type type = o.GetType();
In this example, when you call GetType(), you also will get System.Int32, and not System.Object. Because in your your start with different types before calling GetUnderylingType, you end up with different results.
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.
According to the docs: http://msdn.microsoft.com/en-us/library/x13ttww7.aspx:
The volatile keyword can be applied to reference types.
So why is it illegal to use on a Nullable<T>.. a reference type!
Please note that I do not actually need volatile semantics on a Nullable<T> field, I encountered this error accidentally and am simply curious.
Nullable<T> isn't a reference type. It's a value type:
public struct Nullable<T>
where T : struct, new()
Note the struct part.
Just because it has a null value doesn't make it a reference type... it's a nullable value type. See section 4.1.10 of the C# 4 language spec for more details.
Nullable is a value type, not a reference type.
See http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx for definition.
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++.