Optional Parameters

In theory, optional parameters can be useful.  If I have a public method, and I want to add a new parameter, I can add it with the Optional keyword.

Public Sub MyFunction(ByVal parameter1 As Integer, Optional ByVal parameter2 As Integer = 0)
End Sub

I’ve added a new bit of functionality, the existing code still works, life is good.

But optional parameters have a darker, more sinister side.  They limit my options.  What if I want to add a new, required parameter to that method?  I can follow the original pattern and add it at the end with the Optional keyword (not that I have a choice, once Optional, always Optional — anything to the right of an Optional parameter must also be Optional):

Public Sub MyFunction(ByVal parameter1 As Integer, Optional ByVal parameter2 As Integer = 0, Optional ByVal parameter3 As Integer = 0)

End Sub

Except now it’s not required, it’s optional.  That’s no good.  Never fear, I’ll just wedge it in before the Optional parameter:

Public Sub MyFunction(ByVal parameter1 As Integer, ByVal parameter3 As Integer, Optional ByVal parameter2 As Integer = 0)

End Sub

Life is good, yes?  No.  What if I was calling the method like this:

Public Sub MyCallingMethod()
    MyFunction(1, 2)
End Sub

Even after adding parameter3 in the middle, this will still compile.  Maybe if we’re lucky QA will stumble across the hidden bug, although I wouldn’t bet my weekend on it.  (And you will be betting a weekend on it.)

If this method were Private, so you have total control and full visibility on where it’s called from, you might get away with adding a parameter in the middle of the list.  You’ll need to be very thorough to get away with it.

Only in the most dire circumstances should you consider this sort of modification in a Public method that could be called from some other code project.  The risk is just not acceptable.  The Optional parameters were a bad enough decision (but that’s a separate email!) – don’t multiply the magnitude of the problem.

The simplest solution is to avoid the issue by avoiding Optional parameters whenever possible.  Failing that, your back up plan is to leave the original method alone.

Public Sub MyFunction(ByVal parameter1 As Integer, Optional ByVal parameter2 As Integer = 0)

End Sub

Then add a new method with a new name and the new parameter added.

Public Sub MyFunctionWithParam3(ByVal parameter1 As Integer, ByVal parameter3 As Integer, Optional ByVal parameter2 As Integer = 0)

End Sub

New method name – no conflicts with any legacy code.

Advertisements

Divide By Zero

I learned something today. This code:

Dim input1 As Double = 5
Dim output1 As Double = (input1 / 0)

…will not throw a System.DivideByZeroException.

It actually returns Double.PositiveInfinity.

Debug.Print(output1.ToString)

…results in “Infinity”.

Debug.Print(Double.IsInfinity(output1))
Debug.Print(Double.IsPositiveInfinity(output1))
Debug.Print(Double.PositiveInfinity = output1)

All result in “True”.

A Single Type has the identical behavior.

In spite of the documentation for System.DivideByZeroException, my testing shows that an Integer Type displays the same behavior.

Dim input1 As Integer = 5
Dim output1 As Double = (input1 / 0)

…will return Double.PositiveInfinity. Be careful with this one – I’m always leery of something that blatantly contradicts the documentation.

The Decimal Type does respond the way I expect:

Dim input1 As Decimal = 5
Dim output1 = (input1 / 0)

…does indeed throw a System.DivideByZeroException.

If you’re using strongly-typed variables, this will rarely be a problem. The danger comes when we don’t strongly type our variables and don’t have Option Strict On. The following is perfectly acceptable code:

Dim input1 As Double = 5
Dim input2 As Double = 0
Dim dangerousOutputString = (input1 / input2).ToString

The output, however, is anything but acceptable: dangerousOutputString now contains the string “Infinity”.