views:

283

answers:

8

I've just started writing on a component where I found it might be useful to declare some of the properties nullable, instead of letting them resort to default values. However, I realized that I've never before used the non-nullable-type? syntax or the Nullable<T> type before, so there are probably some gotchas that'll soon jump out and bite me. So...

  • What are the biggest gotchas when using Nullable<T> and the shorthand ? syntax?

  • How do I work around them?

  • What are the biggest advantages/new possibilities that are made available to me when I start using them?

+1  A: 

you can do .HasValue to check if the variable is null

you can do

 myvar = nullablevar ?? defaultvalue; //will set defaultvalue if nullablevar  is null

and more, it's not new to c# 4

read this for more information

Fredou
Not really a "gotcha" but VERY useful to know
Grant Peters
+18  A: 

A common gotcha is attempting to assign to a nullable variable with a conditional expression as follows:

bool useDefault = true;
int defaultValue = 50;
int? y = useDefault ? defaultValue : null; 

At first glance this might look like it should work but actually it gives a compile error:

Type of conditional expression cannot be determined because there is no
implicit conversion between 'int' and '<null>'

Solution: add a cast to one or both of the possible outcomes:

int? y = useDefault ? defaultValue : (int?)null; 

Less commonly seen: Normally it is safe to assume that for integers a <= 5 and !(a > 5) are equivalent. This assumption is not true for nullable integers.

int? x = null;
Console.WriteLine(x <= 5);
Console.WriteLine(!(x > 5));

Result:

False
True

Solution: Handle the null case separately.


Here's another slight variation of the above:

int? y = null;
int? z = null;
Console.WriteLine(y == z);
Console.WriteLine(y <= z);

Output:

True
False

So y is equal to z, but it's not less than or equal to z.

Solution: Again, treating the null case separately can avoid surprises.

Mark Byers
@Mark Byers, Entity Framework taught me so much about nullable types
msarchet
A: 

I am not currently using 4.0, but in 2.0 I have the problem that, like most Generics, int? can't be de-serialized easily. Perhaps 4.0 is smarter with Reflection, but the default XML serializer utilities can't read it of it is exposed. It is quite annoying.

William Crim
I don't know if it was different in 2.0, but in 4.0 serializing generic types to XML is not a problem at all... serializing a `Foo<string>` results in a `<FooOfString>` element in XML
Thomas Levesque
In 2.0, I get a reflection error from the XML deserializer(I just use the default) using any generic property or field. I have to string[] or ArrayList because List<string> won't serialize/deserialize. If this is fixed in 4.0, my heart will sing with joy. Working on legacy code sucks.
William Crim
A: 

Set default value unconsciously while using nullable. I have seen this a few times. Eg.

int? localVar = 0;
// do something ..
if (localVar.HasValue)
   _myMemberValue = localVar.Value;

The localVar is never null because it was initialize with a value so the test for HasValue is alwasy true and _myMemberValue may incorrectly assigned with incorrect value.

-- Editied, to add more comments ----

Forgot to mention. One major advantage that I have seen is to use the field for representing Nullable field in database. This is also generated automatically if you use Linq and EF. But traditionally before Nullable, it is manual work and error prone to handle update of field and deciding on when to set it with a value or to null.

Fadrian Sudaman
+1  A: 

Nullable<T> is a special value type. It might help if you understand how it actually works. There are a few subtle things about it that are not immediately obvious. I blogged about here.

Actual gotchas - not many. Just about the biggest one is that explicit cast might throw an InvalidOperationException (and not NullReferenceException). The compiler should guide you for any problems that might arise.

Igor Zevaka
+1  A: 

Nullable<T> types are a bit unusual; they are (strictly speaking) neither value nor reference types, but something strange in-between. Boxing/unboxing and typeof in particular have special rules so the results are less unexpected.

For details, I recommend Jon Skeet's book C# in Depth. If you don't already own it, you should. :)

Stephen Cleary
+10  A: 

Something people are often surprised by is that there is no such thing as a boxed nullable value type. If you say:

int? x = 123;
int? y = null;
int z = 456;
object xx = x;
object yy = y;
object zz = z;

you might think that since zz contains a boxed int, that xx and yy contain boxed nullable ints. They do not. xx contains a boxed int. yy is set to null.

Eric Lippert
+1  A: 

One of the best uses is that it maps quite well to nullable database fields -- keeping that in mind, you want to use it as you would a null field in a database.

That is, a null value is not "other" -- it means unknown, or incalculable at this time, or that given other values in this object/record, it doesn't make sense for this to be a value. Your situation sounds like it is valid -- user preferences can be null, and actual values will be

actualValue = userValue ?? defaultValue;

The ?? is the null coalesce operator -- the above is equivalent to the following:

actualValue = userValue.HasValue ? userValue : defaultValue;

or

actualValue = (userValue != null) ? userValue : defaultValue;

The temptation I see people give into most often is using bool? as a tri-state flag. This doesn't make sense, because there is no third possibility in true/false/???. Others reading your code will have to dig through comments (best case scenario) to find out that true meant to use blue text, false meant red text, and null meant green text. Use enums for this.

That's the biggest trap I see people fall into -- other than that, just get used to checking if your nullables are null -- either through comparison to null or by using .HasValue

Peter Leppert
+1 for the note about `bool?` as a tri-state flag =)
Tomas Lycken