1

With VB's Option Strict On, why does a Nullable(Of T) not require an explicit cast to an interface of T when it does require one to T?

I.e.

Dim x As Integer? = 5
Dim y As Integer
Dim z As IComparable

y = x ' Fails to compile with error
      ' "Option Strict On disallows implicit conversions from 'Integer?' to 'Integer'."
z = x ' Succeeds

EDIT: As (sort of) shown by @SSS, part of the answer is that Nullable values are, well, nullable, and can be Nothing, which is fine for a reference like an interface. So this conversion will always succeed, unlike the conversion to T case (which fails when the Nullable has no value), and so it can be seen as an implicit conversion.

My question now becomes "how?". How is the conversion from a Nullable(Of T) (which has no interfaces of its own) to an interface of T theoretically negotiated?

I know the implementation is box Nullable<T>, which effectively strips the Nullable wrapper, but I'm confirming the concept here...

(So I'll review the documentation and see if they explain this.)

Mark Hurd
  • 10,665
  • 10
  • 68
  • 101
  • 1
    Is this in fact the VB.NET version of [this 'bug'](http://stackoverflow.com/q/10182898/256431)? – Mark Hurd Sep 20 '12 at 01:45
  • I should add: I know `Nullable` values are easily converted to the real values by the runtime, because when I play with them with DotLisp (which has not been updated to explicitly cater for `Nullable` types) they quickly disappear... – Mark Hurd Sep 20 '12 at 08:30

3 Answers3

1

I don't see the problem?

y = x 

can fail because x could hold a value of Nothing, but y is not allowed to hold a value of Nothing. The IComparable interface allows Integers to be compared to Nothing however, so that assignment is fine.

Notice that if you swap it round:

x = y

then this succeeds because every value of y can be assigned to x.

You can confirm that Integers can be compared to Nothing as follows:

MsgBox(5.CompareTo(Nothing))
SSS
  • 4,807
  • 1
  • 23
  • 44
  • 1
    I can see your point, sort of, but your comparison to the _literal_ `Nothing` is effectively irrelevant. That is "literally" the same as `5.CompareTo(0)`, which can be more easily seen if you check `0.CompareTo(Nothing)` compared to `Dim n As Integer? = Nothing : MsgBox(0.CompareTo(n))`! Your point _is_ (sort of) made by this latter test though... – Mark Hurd Sep 20 '12 at 08:02
  • 1
    Further reading: http://stackoverflow.com/questions/9012934/why-does-the-operator-work-for-nullablet-when-is-not-defined If Eric Lippert doesn't know, noone knows :-) – SSS Sep 21 '12 at 00:51
  • 1
    "Nullable is a very special type and its magical properties are embedded deeply within the C# language and the runtime." is the key quote I think. – SSS Sep 21 '12 at 00:54
  • I think the quote "The boxing semantics of nullables are deeply weird and baked into the runtime." from that answer is relevant here. Good find. – Mark Hurd Sep 21 '12 at 03:16
0

From what I can tell in vb.net, the statement interfaceVariable = nullableVariable is essentially equivalent to interfaceVariable = if(nullableVariable.HasValue, CType(nullableVariable.Value, interfaceType), Nothing). The C# compiler seems to handle things the same way: interfaceVariable = nullableVariable; becomes interfaceVariable = nullableVariable.HasValue ? (interfaceType)nullableVariable.Value : null;.

If the type of nullableValue.Value implements the interface, then nullableVariable.Value will either perform return a value-type result or throw an exception. Since there exists a guaranteed boxing conversion from the return value to the interface, the cast will be legal. The only way the above code could fail would be if the nullable variable gets written between the calls to HasValue and Value, such HasValue sees the variable as non-null, but Value sees it as null and throws an exception. I believe that writing interfaceVariable = nullableVariable just tests nullity once, so that an exception could not occur; instead, an indeterminate value would get boxed.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • (Be careful that you're compiling with `Option Strict On`.) The VB.NET IL for the assignment is effectively `interfaceVariable = (interfaceType)(object)nullableVariable` (using C# casts because they're easier to type); i.e. it just "boxes" the nullable then casts it to the interface. The boxing does the magic. – Mark Hurd Sep 21 '12 at 03:28
0

Without actually reading documentation yet, I'm going to attempt an answer:

Firstly, the higher-level answer is that casting a Nullable to an interface is "safe" and will not throw, so it is logically a Widening operator and should not need to be explicit (compared to casting to T, when .HasValue is False it throws, so it should not be implicit with Option Strict On).

However, technically the "how" is a bit obscure: Even though some of the behaviour of Nullable is encoded in the metadata available via reflection, much of its "magic" is hidden in:

  • the runtime behaviour of box on a Nullable (and thus the compiler knows when to leave "lifting" to that), and
  • the other points made by Eric Lippert in his answer for C# and their equivalent in VB.NET.

It looks like S. Somasegar's blog post announcing changes to Nullable support in a late beta release for VS2k5 is also relevant here.

Community
  • 1
  • 1
Mark Hurd
  • 10,665
  • 10
  • 68
  • 101