14

In VB.NET, I have to compare some objects within a select case statement.

Since select case uses = operator by default and this is not defined for objects, a compile error is thrown.

I presently use this workaround:

Select Case True
    Case sender Is StyleBoldButton

    Case sender Is StyleUnderButton

    Case sender Is StyleItalicButton

End Select

which actually works.

Is there something prettier to see and more understandable?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Teejay
  • 7,210
  • 10
  • 45
  • 76
  • 1
    There probably is a better way using polymorphism. What is it that you're ultimately doing with `sender`? Maybe you can abstract that functionality into a single operation that would work on each type of button? Then you wouldn't need the `Select` but would just perform that operation regardless of the type? – David Jul 03 '12 at 10:25
  • 1
    `If sender Is StyleBoldButton ElseIf .... End If` ? Prettier to see maybe not, more understandable probably yes, since the `Select Case True` trick is not AFAIK very widely used in VB.NET – Paolo Falabella Jul 03 '12 at 10:27
  • @David Mmmhh... I need to set a different variable for each button. But BOLD and ITALIC are boolean, while UNDERLINE is a enuberable type. – Teejay Jul 03 '12 at 10:29
  • @PaoloFalabella the `Select case True` was widely used in VB6 and earlier to simulate a short-circuit If, like we currently do with AndAlso/OrElse. But yes, not widely used in .net. – Teejay Jul 03 '12 at 10:33
  • @Teejay: I'd have to know more about the code to be more specific, but more often than not a `Select` is an opportunity to use the Strategy Pattern. You could create an abstract base strategy class which exposes an abstract function to perform whatever action needs to be performed. Then create strategy implementations of that class for each of these types. But how you'd wire that up with your existing code isn't really clear here. I'm not sure how you'd feed `sender` to the abstraction in order for it to determine the implementation. – David Jul 03 '12 at 10:37
  • @David I've got 3 button, one for each text style, which I'd prefer to handle with a single method. I only need to set a variable when one of them is checked, maybe creating an abstract strategy class sounds a little excessive for this task.. isn't it? – Teejay Jul 03 '12 at 10:56
  • @teejay What prompts you to use a single method ? Event handlers are specifically designed to deal with this. If you have some common comportment between the three handlers, just keep it in a separate method that you call from each of the three handlers. (that's how I'd do it anyway...) – Denis Troller Jul 03 '12 at 11:34
  • Dont use the GUI as a replacement for business logic. This is why you are encountering problems. By binding controls to business objects you can more easily reuse the functionality, ease testing and make the GUI light and small. – Jeremy Thompson Jul 04 '12 at 05:54
  • With these buttons, I'm only commanding a UI component (excel workbook like), in fact the code in each `Select` section is something like `WorkbookView.RangeSelection.Font.Bold = sender.Checked`, then the component will handle business logic itself. For other buttons that involves custom business logic and model modifications, I already use a MVC pattern. – Teejay Jul 05 '12 at 07:04
  • Anyway, the question is really general. Please don't consider the case itself. – Teejay Jul 05 '12 at 07:11

4 Answers4

11

Anything that has the requisite comparison operators (=, >=, <=, etc.) defined is fair game for Select Case. Rightly (or wrongly), references just aren't compared with = in VB; one must use Is. (Or Object.Equals(objA As Object, objB As Object) - but, really, why? when you've got Is?)

But take a look at Object equality behaves different in .NET - perhaps the VB way is less confusing? Whatever, I think you're stuck with the If-ElseIf ladder since Select Case doesn't do Is. (well, it does, but that's a different Is, more like the it of Hypercard.) I think the ladder looks smart and easy to follow:

If sender Is StyleBoldButton Then 

ElseIf sender Is StyleUnderButton Then

ElseIf sender Is StyleItalicButton Then

Else

End If 

As you have pointed out, the Select Case True pattern is an "OrElse" short-circuit workaround in VB6 - a wonky way to meet a real need. But that's not needed in VB.NET. In that spirit, maybe it's better to use design patterns more in line with the best practices expected of an object-oriented language. For example, as Denis Troller suggested, why not give each button its own event handler?

But if you insist on something like an Is-able Select, here's something I probably won't use myself:

With sender
    If .Equals(StyleBoldButton) Then

    ElseIf .Equals(StyleUnderButton) Then

    ElseIf .Equals(StyleItalicButton) Then

    Else

    End If
End With

Here I'm counting on .Equals to work like the C# == when faced with two object types to compare (see http://visualstudiomagazine.com/articles/2011/02/01/equality-in-net.aspx). The beauty of this is that sender is mentioned only once; however there's all this ElseIf .Equals( ... ) Then you'll have to type for each "Case".

Another way I won't use myself is using GetHashCode():

Select Case sender.GetHashCode()

    Case StyleBoldButton.GetHashCode()

    Case StyleUnderButton.GetHashCode()

    Case StyleItalicButton.GetHashCode()

    Case Else

End Select

Here I'm counting on what (very) little I know of GetHashCode() to uniquely (enough) identify these controls. (See Default implementation for Object.GetHashCode() ).

Community
  • 1
  • 1
rskar
  • 4,607
  • 25
  • 21
  • 1
    Probably the best answer to my question would have been "You can't". But your solutions are just fine :) – Teejay Jul 09 '12 at 08:59
  • GetHashCode should not be relied on to identify an object. The hashcode could be the same for different objects. – jo0ls Jul 07 '15 at 09:49
7

I just came across this same problem. After seeing another post and this post I came to this solution for myself and I wanted to share in case someone out there really wanted to use the Select Case like I did :)

    Select Case DirectCast(sender, Button).Name
        Case StyleBoldButton.Name

        Case StyleUnderButton.Name

        Case StyleItalicButton.Name

    End Select

Update 6-16-16: Removed "Is = " because it was unnecessary.

Update 8-27-16: Changed the use of strings to use .Name for better error tracking.

Jeffrey
  • 528
  • 5
  • 14
  • What does `Case Is = "StyleBoldButton"` give you over `Case "StyleBoldButton"`? The `Is` is unnecessary and thus irrelevant to the question. – Zev Spitz Jun 15 '16 at 14:40
  • Because my brain got stuck on the OPs solution of object comparison instead of realizing that my solution was a string comparison so I put the Is operator in there without really thinking about it. – Jeffrey Jun 16 '16 at 15:52
  • At-least use `Case StyleBoldButton.Name` instead so you don't get unexpected results if (or rather when) the name changes. – NiKiZe Aug 26 '16 at 15:56
  • This is another good suggestion, I'll update my answer to reflect that. – Jeffrey Aug 27 '16 at 18:28
  • I use this from and think it's the most readable and flexible. A couple of things about the example here: 1. Better to use TryCast in a wrapping If in case sender is not a Button control (eg `If TryCast(sender, Button) IsNot Nothing then ...`) 2. Possibly better than that is just cast to Control: `Select Case TryCast(sender, Control) ...` – SteveCinq Jan 31 '17 at 00:01
  • if I could thumbs up this again, I would. I love this because the "ugly" part is as the end, and its short and sweet. It lets me convey the original intent that is implied by a Select branch (based on this value, do one of these things). Its excellent. I imagine the only better option is for VB to allow Is to work. – Brent Larsen May 19 '21 at 21:50
-1

Less concise, but much more readable:

if typeof(sender) is StyleBoldButton then

elseif typeof(sender) is StyleUnderButton then

elseif typeof(sender) is StyleItalicButton then

else

end if
ssis_ssiSucks
  • 1,476
  • 1
  • 12
  • 11
  • 3
    I believe OP was asking about (syntactic sugar for) comparing references (instances of Objects), not types (classes of Objects). Besides, an If-Then-ElseIf ladder was already suggested in comments above. – rskar Jul 03 '12 at 17:06
  • @rskar Exactly, i need to compare references, not types. BTW, in case of type comparation, typeof(sender).toString() is probably the best way. – Teejay Jul 06 '12 at 13:54
-2
 Private Sub btnNum_Click(sender As Object, e As EventArgs) Handles btnNum0.Click, btnNum1.Click, btnNum2.Click, btnNum3.Click, btnNum4.Click, btnNum5.Click, btnNum6.Click, btnNum7.Click, btnNum8.Click, btnNum9.Click, btnDicemalPoint.Click, btnNumClear.Click, btnExit.Click
        If result = "0" Then
            result = ""
        End If
        Select Case True
            Case sender Is btnNum0
                If result <> "0" Then
                    result = result & "0"
                End If
            Case sender Is btnNum1
                result = result & "1"
            Case sender Is btnNum2
                result = result & "2"
            Case sender Is btnNum3
                result = result & "3"
            Case sender Is btnNum4
                result = result & "4"
            Case sender Is btnNum5
                result = result & "5"
            Case sender Is btnNum6
                result = result & "6"
            Case sender Is btnNum7
                result = result & "7"
            Case sender Is btnNum8
                result = result & "8"
            Case sender Is btnNum9
                result = result & "9"
            Case sender Is btnDicemalPoint
                If String.IsNullOrEmpty(result.ToString) Then
                    result = result & "0."
                ElseIf Not result.ToString.Contains(".") Then
                    result = result & "."
                End If
            Case sender Is btnNumClear
                result = 0
            Case sender Is btnExit
                Me.Close()
        End Select
End Sub