I've just started writing on a 开发者_JAVA百科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?
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.
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.
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
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.
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. :)
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
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.
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.
精彩评论