4

In Nullable micro-optimizations, part one, Eric mentions that Nullable<T> has a strange boxing behaviour that could not be achieved by a similar user-defined type.

What are the special features that the C# language grants to the predefined Nullable<T> type? Especially the ones that could not be made to work on a MyNullable type.

Of course, Nullable<T> has special syntactic sugar T?, but my question is more about semantics.

Eldritch Conundrum
  • 8,452
  • 6
  • 42
  • 50
  • 1
    It was by pure chance that I found your question here. Consider leaving questions like this on the blog posting itself. Also, feel free to email me if you have a question about a posting; there's contact information on the blog's "About" page. – Eric Lippert Dec 20 '12 at 21:08
  • I did consider it, but I also wanted to learn more about special support other than boxing, so I turned to Stack Overflow. – Eldritch Conundrum Dec 20 '12 at 21:16

3 Answers3

11

What I was getting at is: there is no such thing as a boxed nullable. When you box an int, you get a reference to a boxed int. When you box an int?, you get either a null reference or a reference to a boxed int. You never get a boxed int?.

You can easily make your own Optional<T> struct, but you can't implement a struct that has that boxing behaviour. Nullable<T>'s special behaviour is baked into the runtime.

This fact leads to a number of oddities. For example:

And FYI there are other ways in which the Nullable<T> type is "magical". For instance, though it is a struct type, it does not satisfy the struct constraint. There's no way for you to make your own struct that has that property.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I see... So if I understand correctly, optimization is the reason for these odd behaviours. Thanks for the explanation! – Eldritch Conundrum Dec 20 '12 at 21:12
  • 2
    @EldritchConundrum: Not really. Rather, the oddities are a consequence of the mismatch between nullable value semantics and nullable reference semantics. The moral of the story is: add nullability to a type system in v1.0, not v2.0. If nullable reference types and nullable value types were invented at the same time they'd be a lot more similar. – Eric Lippert Dec 20 '12 at 21:24
  • @EricLippert: How do you think things would be done differently? My biggest objection to the design of `Nullable` is that--at least by my understanding--in most cases where code gets a `Nullable`, what it wants is a `T` with an indication of whether it's valid; much of the extra stuff around `Nullable` gets in the way of that basic mission. Do you see things differently? – supercat Dec 22 '12 at 02:28
2

I found these two in the C# specifications:

  • The is operator works on T? as it would have on T, and the as operator can convert to nullable types.
  • Predefined and user-defined operators that operate on non-nullable value types are lifted to the nullable forms of those types.

Now, here are the features that I think are not limited to Nullable<T>:

  • The value in a switch can be of a nullable type. I don't think this counts, because switch also accepts user-defined implicit conversions that could be defined on a MyNullable type.
  • Nullable IDisposable types are supported, with a null check being inserted before the generated calls to Dispose(). I don't think this counts, because I could define MyNullable as a class and then it would be the same.

Here is what I am not sure about:

  • The specs mentions boxing/unboxing and implicit/explicit conversions, but I do not understand whether the same results can be achieved with a MyNullable.
Eldritch Conundrum
  • 8,452
  • 6
  • 42
  • 50
  • There is no way to specify that a value type other than `Nullable` should not be convertible to `Object`, or that such the result of conversion should not be an object with the same fields as the struct, initialized to the same values. – supercat Dec 22 '12 at 04:06
2

C# lifts operators on nullable types. For example:

int? SumNullableInts(int? a, int? b)
{
    return a + b;
}

You would have to do a lot of reflection work in MyNullable<T> to support that, and then the following would compile, where it shouldn't:

MyNullable<List<string>.Enumerator> SumNullableEnumerators(MyNullable<List<string>.Enumerator> a, MyNullable<List<string>.Enumerator> b)
{
    return a + b;
}
phoog
  • 42,068
  • 6
  • 79
  • 117