Using a ParamArray, but requiring at least one parameter
Asked Answered
G

2

5

What I used to have:

Public Sub Subscribe(channel As ChannelType)
Public Sub Subscribe(channels As IEnumerable(Of ChannelType))

The first one just calls the second one with {channel} to convert its parameter into an array.

I decided that having to create a list of channels to pass to the method was awkward and chose to combine the two overloads into one method that takes a ParamArray.

Public Sub Subscribe(ParamArray channels() As ChannelType)

'Usage
Subscribe(ChannelType.News)
Subscribe(ChannelType.News, ChannelType.Sports)
Subscribe() 'Oops... this is valid

What is the "best practice" here? I like the flexibility that ParamArray gives me in just letting people pass stuff in, but it fails to help the developer "fail-faster" via compiler error feedback... that means that something like an ArgumentException is out of the question here since people consuming this method may not be writing any unit tests. One options is the following...

Public Sub Subscribe(channel As ChannelType)
Public Sub Subscribe(channel As ChannelType, ParamArray channels() As ChannelType)

But I feel like that puts me nearly back to square one, is confusing, and requires my implementation of that method to be less straight-forward.

Godwit answered 9/10, 2012 at 18:35 Comment(0)
L
12

Another option to consider would be

Module ParamArrayTest
    Sub ShowThings(ParamArray MyThings() As Integer)
        For Each thing As Integer In MyThings
            Debug.Print("{0}", thing)
        Next
    End Sub

    ' Don't try to call without parameters:
    <Obsolete("Must have at least one parameter", True)> Sub ShowThings()
        Throw New ArgumentException("Must specify at least one parameter")
    End Sub

    Sub Test()
        ShowThings(3, 4, 5)
        ShowThings()
    End Sub
End Module

The <Obsolete()> tag with a second parameter of True informs the compiler that attempting to use the marked method should result in a compilation error. Since the method in question would be used when, and only when, an attempt is made to invoke the method without any parameters, it would cause an error only at such times. Note that method will not be used if an attempt is made to pass the method a zero-element array of Integer; in that case, the normal ParamArray form would be used.

Loveless answered 9/10, 2012 at 23:17 Comment(4)
I think this answer gets it... sorry @Meta-Knight! I like how I get compiler feedback and I get the straight-forward ease of use with being able to simply iterate over the ParamArray instead of having to concat a single item with the ParamArray to iterate over my arguments.Godwit
@JeffBridgman: Note that this code, unlike the approach with a separate argument, makes it possible for code to pass in an array containing all the parameters rather than having to pass the first item and an array containing the rest; as a consequence, while it can disallow invocation with an empty argument list, it cannot at compile time disallow invocation with an empty array.Loveless
What if the ParamArray is the second parameter of the method and you want the paramarray to require a param? Sub ShowThings(SomeValue As String, ParamArray MyThings() As Integer)Hushaby
@tszoro: In that case, have an Obsolete-tagged overload Sub ShowThings(SomeValue As String).Loveless
P
6

I think that the option that you mentioned is the best option. Using clearer names for your parameters will make it less confusing:

Public Sub Subscribe(mainChannel As ChannelType, ParamArray otherChannels() As ChannelType)

The other option is to enforce it at run-time, but as you said it wouldn't fail as fast:

Public Sub Subscribe(ParamArray channels() As ChannelType)
    If channels.Count = 0 then
        Throw new InvalidOperationException("At least one channel is needed")
    End If
End Sub
Parlando answered 9/10, 2012 at 18:41 Comment(1)
Good answer! If I had a problem-domain where there was a primary channel & additional channels, the first option you mentioned would be perfect!Godwit

© 2022 - 2024 — McMap. All rights reserved.