Nullable properties vs. Nullable local variables - c#

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.

Related

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

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.

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++.

What is the difference of getting Type by using GetType() and typeof()? [duplicate]

This question already has answers here:
Type Checking: typeof, GetType, or is?
(15 answers)
Closed 1 year ago.
Which one is the preferred way to get the type?
You can only use typeof() when you know that type at compile time, and you're trying to obtain the corresponding Type object. (Although the type could be a generic type parameter, e.g. typeof(T) within a class with a type parameter T.) There don't need to be any instances of that type available to use typeof. The operand for typeof is always the name of a type or type parameter. It can't be a variable or anything like that.
Now compare that with object.GetType(). That will get the actual type of the object it's called on. This means:
You don't need to know the type at compile time (and usually you don't)
You do need there to be an instance of the type (as otherwise you have nothing to call GetType on)
The actual type doesn't need to be accessible to your code - for example, it could be an internal type in a different assembly
One odd point: GetType will give unexpected answers on nullable value types due to the way that boxing works. A call to GetType will always involve boxing any value type, including a nullable value type, and the boxed value of a nullable value type is either a null reference or a reference to an instance of a non-nullable value type.
GetType() works at runtime, typeof() is a compile-time operator.
So,
// untested, schematic
void ShowType(Object x)
{
Write(x.GetType().Name); // depends on actual type
// typeof(x) won't actually compile
Write(typeof(x).Name); // always System.Object
}
ShowType("test");
Will print System.String and System.Object.
See this question for a better example.
GetType is a virtual method on Object - this means given an instance of a class, you can retrieve the corresponding Type object.
typeof is a C# operator - this is used to perform a compile time lookup i.e. Given a Symbol representing a Class name, retrieve the Type object for it.
if (typeof(String) == "test".GetType())
It's not exactly the same, and the problem appears when you use
inheritance.
I.e.:
WebPage1 inherits from Page, and this one inherits also from Object, so if you test for (new WebPage1()).GetType() == typeof(object) it'll return false because the types are diferent, but when you test using the is operator it's true.
((new WebPage1()) is object) is true because (new WebPage1()) is an object of type WebPage1, and also a Page and an object.
The types might be different, but is checks if you can cast safely to
this type.

Why can't I use the as keyword for a struct?

I defined the following struct:
public struct Call
{
public SourceFile caller;
public SourceFile callee;
public Call(SourceFile caller, SourceFile callee)
{
this.caller = caller;
this.callee = callee;
}
}
Later, I assign it to the Tag property of another object:
line.Tag = new Call(sf1, sf2);
But when I try to retrieve the Tag property like so,
Call call = line.Tag as Call;
Visual Studio gives the following compile-time error:
The operator as must be used within a
reference type or nullable type
What is the meaning of that? And how can I solve it?
Some of the existing answers aren't quite right. You can't use non-nullable types with as, because the result of as is the null value of the type if the first operand isn't actually of an appropriate type.
However, you can use as with value types... if they're nullable:
int a = 10;
object o = a;
int? x = o as int?; // x is a Nullable<int> with value 10
long? y = o as long?; // y is a Nullable<long> with the null value
So you could use:
Call? call = line.Tag as Call?;
Then you can use it as:
if (call != null)
{
// Do stuff with call.Value
}
Two caveats though:
In my experience this is slower than just using is followed by a cast
You should seriously reconsider your current Call type:
It's exposing public fields, which is generally poor encapsulation
It's a mutable value type, which is almost certainly a mistake
I would strongly suggest you make it a class instead - at which point this problem goes away anyway.
Another thought: if the tag should always be a Call, then it's better to cast it:
Call call = (Call) line.Tag;
That way, if the data doesn't match your expectation (i.e. there's some bug such that the Tag isn't a Call) then you get to find out about it early, rather than after you've potentially done some other work. Note that this cast will behave differently depending on whether Call is a struct or a class, if Tag is null - you can cast a null value to a variable of a reference type (or a nullable value type), but not to a non-nullable value type.
A struct is a value type, so it cannot be used with the as operator. The as operator must be able to assign a value of null if the cast fails. This is only possible with a reference type or a nullable value type.
There are a couple ways to solve this, but your best bet is to change your Call type from a struct to a class. This will essentially change your type from a value type to a reference type, which allows the as operator to assign a value of null if the cast fails.
For more information on value types vs. reference types, this is a decent article. Also, have a look on MSDN:
value types
reference types
as-operator
nullable types.
From the C# Spec
§7.10.11 The as operator is used to
explicitly convert a value to a given
reference type or nullable type. Unlike a cast expression
(§7.7.6), the as operator never throws
an exception. Instead, if the
indicated conversion is not possible,
the resulting value is null.
References and nullable types can be null. Stucts are value types so they can't be null.
Call? call = line.Tag as Call?;
It's a limitation of C#. If the type were a reference type, then if the cast failed it would simply return 'null', but since it's a value type, it doesn't know what to return when the cast fails.
You must replace your use of as with two: 'is' and 'as'
if (line.Tag is Call) {
call = (Call)line.Tag;
} else {
// Do whatever you would do if as returned null.
}
What is the meaning - As stated, structures are value types.
How can I solve it - Change it to
Call call = line.Tag;

Categories

Resources