views:

1849

answers:

5

Why it is not allowed to assign null to a DateTime in C#? How has this been implemented? And can this feature be used to make your own classes non-nullable?

Example:

string stringTest = null; // Okay
DateTime dateTimeTest = null; // Compile error

I know that I can use DateTime? in C# 2.0 to allow null to be assigned to dateTimeTest and that I could use Jon Skeet's NonNullable class on my string to get a run time error on the assignment of stringTest. I'm just wondering why the two types behave differently.

+31  A: 

DateTime is a value-type (struct), where-as string is a reference-type (class etc). That is the key difference. A reference can always be null; a value can't (unless it uses Nullable<T> - i.e. DateTime?), although it can be zero'd (DateTime.MinValue), which is often interpreted as the same thing as null (esp. in 1.1).

Marc Gravell
Thanks a lot for the clarification. Why not use structs to make non-nullable classes?
Jan Aagaard
And what would default(YourWrapper) be? ;-p It would be a struct containing a null reference... all structs *always* have a default constructor...
Marc Gravell
(or *never* have a default constructor, depending on whether you are talking about C# or the CLI - which disagree over this point)
Marc Gravell
Marc, your answers are simply great. Concise and to the point!
Cherian
+5  A: 

DateTime is a struct and not a class. Do a 'go to definition' or look at it in the object browser to see.

HTH!

Simon
+1  A: 

DateTime is a value type, same as an int. Only reference types (like string or MyCustomObject) can be null. Reference types really store "references" to the objects location on the heap.

here's a article I found that explains it better. and here's the MSDN article on it

danswain
Or nullable value types via Nullable<T> (which is itself also a struct)
Marc Gravell
A: 

string is a class whereas DateTime is a structure. Thats why you cannot set it to null

Rob Haupt
+2  A: 

The important distinction between ValueTypes and reference types is that value types have these "value semantics". A DateTime, Int32 and all other value types have no identity, an Int32 "42" is essentially indistinguishable from any other Int32 with the same value.

All value type "objects" exist either on stack or as a part of a reference type object. One special case is when you cast a value type instance to an Object or an interface - this is called "boxing", and it simply creates a dummy reference-type object which only contains the value that can be extracted back ("unboxed").

Reference types, on the other hand, have an identity. a "new Object()" does not equal any other "new Object()", because they are separate instances on the GC heap. Some reference types provide Equals method and overloaded operators so that they behave more value-like, eg. a String "abc" equals other "abc" String even if they are in fact two different objects.

So when you have a reference, it can either contain the address of a valid object, or it can be null. When value type objects are all-zero, they are simply zero. Eg. an integer zero, a float zero, Boolean false, or DateTime.MinValue. If you need to distinguish between "zero" and "value missing/null", you need to use either a separate Boolean flag, or, better yet, use the Nullable<T> class in .NET 2.0. Which is simply the value plus a Boolean flag. There's also support in the CLR so that boxing of a Nullable with HasValue=false results in a null reference, not in a boxed structure with false+zero, as it would if you were to implement this structure yourself.

jachymko