6

Why is this different?!

Public Class Form1
 Public Function MyFunction() As Integer?
    Return Nothing
 End Function

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim o As Object = Me
    MsgBox(TypeName(Me)) ' Form1
    MsgBox(TypeName(o))  ' Form1
    MsgBox(TypeName(Me.MyFunction())) ' Nothing
    MsgBox(TypeName(o.MyFunction()))  ' Nothing
    ' but
    MsgBox(TypeName(Me.MyFunction() + 0)) ' Nothing
    MsgBox(TypeName(o.MyFunction() + 0))  ' Integer
 End Sub
End Class
Edper
  • 9,144
  • 1
  • 27
  • 46
Babak A
  • 61
  • 3
  • 1
    Does `o.MyFunction()` work? – CodeCaster Dec 18 '14 at 11:21
  • It works, but method does not appear in Intellisense. – Fabian Bigler Dec 18 '14 at 11:23
  • @fabigler That means it has to be instantiated for it to work? `Dim o as New Object = Me` ? – Edper Dec 18 '14 at 11:27
  • 2
    @Edper That's invalid syntax - `Me` is already an instance – James Thorpe Dec 18 '14 at 11:28
  • You actually don't need `o` at all, you can do the cast inline `MsgBox(TypeName(CType(Me, Object).MyFunction() + 0))` and get "Integer" as well. – James Thorpe Dec 18 '14 at 11:31
  • @JamesThorpe: You're right. – Edper Dec 18 '14 at 11:46
  • The last call is the only one where you're going to end up invoking the [AddObject](http://msdn.microsoft.com/en-US/8zx6ct68.aspx) method because it's a late-bound call. Unfortunately, it's not documented (that I can find) what that method will actually do and I'm not able to make much sense of it yet via Reflector. – Damien_The_Unbeliever Dec 18 '14 at 11:47
  • Worth noting that the [source](http://referencesource.microsoft.com/#Microsoft.VisualBasic/Helpers/Operators.vb,2847) is now available, rather than using Reflector – James Thorpe Dec 18 '14 at 11:58
  • This is probably a contrived example but you should avoid using Objects in favour of a strongly typed variable instead. Switching option strict on will help you to find these issues at design time to and avoid this sort of nastyness cropping up at runtime on a client machine – Matt Wilko Dec 18 '14 at 14:30

2 Answers2

4

Using Option Strict On is a pretty good way to avoid surprises like this. You'll get a "what the heck are you trying to do?" error message from the compiler.

But with it Off, these are valid statements, executed by the DLR, the Dynamic Language Runtime. Which is capable of evaluating late-bound expressions like this. It however has a problem with a nullable type like Integer?. It needs to deal with the boxed version of the value. Which is just plain Nothing. And Nothing doesn't have any type information associated with it. There's nothing the DLR can do to see that this started life as a nullable integer, for all it knows it could be a string that is Nothing.

The compiler cannot help either, it cannot emit any code to make the expression follow normal evaluation rules. All it knows is that there is some function, it doesn't know which, whose name is "MyFunction" with no idea what kind of value it returns. It passes the buck to the DLR to sort it out.

So the DLR just punts at it. And it comes up with "No idea" + 0 = 0. Given that it does have type information for 0. It is an Integer so it tries to interpret the left operator as an integer as well. Which is valid, Nothing is a correct default value for Integer.

Feature, not a bug.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thank you for giving full details! – Babak A Dec 18 '14 at 12:19
  • 1
    `Option Strict On` can also be enabled project wide under `Project -> Properties -> Compile` also you can configure Visual studio to set it to true when you create new projects `Tools -> Options -> Projects and Solutions -> VB Defaults` i highly recommend setting both `Explicit` and `Strict` to `On`, this way the compiler will complain more but i prefer a compiler error instead of a runtime error! – Peter Dec 18 '14 at 12:36
2

Visual Basic .NET had Nothing long before it had nullable value types - it inherited it from pre-.NET Visual Basic. And in some cases, it behaves more like C#'s default(T) then t does null.

Your final call is invoking the AddObject method in the Visual Basic compiler services. This method has existed for a long time, and again pre-dates nullable value types, and unfortunately isn't well documented.

Unfortunately, they couldn't make nullable types behave absolutely consistently, especially in the face of late-bound calls, whilst still maintaining backwards compatibility. For instance, this also prints 0:

Console.WriteLine(CType(CType(Nothing, Object), Int32))
Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • It's documented here [AddObject](http://referencesource.microsoft.com/#Microsoft.VisualBasic/Helpers/Operators.vb,36e9a5a44cca4eb6) on or about line 2847 – Chris Dunaway Dec 19 '14 at 14:44
  • @ChrisDunaway - I wouldn't describe a *current* implementation as documentation. I've linked to the documentation and it is, shall we say, sparse. It tells you nothing about what behaviour is *guaranteed* across current and future implementations. The source just tells you about current behaviour. – Damien_The_Unbeliever Dec 19 '14 at 14:48
  • I looked over the source and, you're right, I would agree that it doesn't really constitute _documentation_. – Chris Dunaway Dec 19 '14 at 14:51