Conditionally skip cases - vb.net

In a Select...Case statement, is there a way to skip cases based on a precondition?
What I'm doing now, using an incredibly stupid example:
Private Sub PrintNumbers(includeEvenNumbers As Boolean, includeOddNumbers As Boolean)
For number As Integer = 0 To 9
Select Case number
Case 0, 2, 4, 6, 8
If includeEvenNumbers Then
Console.WriteLine(number)
End If
Case 1, 3, 5, 7, 9
If includeOddNumbers Then
Console.WriteLine(number)
End If
End Select
Next
End Sub
Sometimes I'll even write my cases inside out:
Private Sub PrintNumbers(includeEvenNumbers As Boolean, includeOddNumbers As Boolean)
For number As Integer = 0 To 9
Select Case True
Case includeEvenNumbers
If number Mod 2 = 0 Then
Console.WriteLine(number)
End If
Case includeOddNumbers
If number Mod 2 <> 0 Then
Console.WriteLine(number)
End If
End Select
Next
End Sub
What I'd really like to do instead:
Private Sub PrintNumbers(includeEvenNumbers As Boolean, includeOddNumbers As Boolean)
For number As Integer = 0 To 9
Select Case number
Case 0, 2, 4, 6, 8 When includeEvenNumbers
Console.WriteLine(number)
Case 1, 3, 5, 7, 9 When includeOddNumbers
Console.WriteLine(number)
End Select
Next
End Sub
Notice that I used the When keyword, which is currently only used in Try...Catch blocks.
Can this be done? Who do I talk to to make this happen?
EDIT (1/2)
What's important is that this code would first evaluate When <expression>. Only if that evaluates to True, it would go on to evaluate the Case <expression>.
The main reason why I want to do this is because I'd like to write cases where the test condition throws an exception if the circumstances are right (or wrong, depending on how you look at it). I'd like to skip those cases if the precondition is true.
EDIT (2/2)
It's pretty clear by now that what I'm asking for is not possible in the current iteration of VB. So I searched for a place to submit feature requests to the .NET development team, then found out they have a uservoice platform.
Long story short: if you'd like to see this implemented, tap the vote button on this page: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4274712-add-when-keyword-support-to-select-case-stat

example of linq:
Dim condition As Predicate(Of Integer) = Nothing
If includeEvenNumbers Then
condition = Function(x) x Mod 2 = 0
Else
condition = Function(x) x Mod 2 = 1
End If
For Each item In Enumerable.Range(0, 8).Where(condition)
' do what ever you like here...
Next
EDIT: to make it simpler:
For Each item In Enumerable.Range(0, 8).Where(Function(x) x Mod 2 = If(includeEvenNumbers, 0, 1))
' do what ever you like here...
Next

By using an enum instead of 2 booleans you only need one if statement to accomplish a 2 condition check. Something like this:
Public Enum Include
Odd = 1
Even = 2
Both = 0
End Enum
Private Sub PrintNumbers(includenumbers As Include)
For I = 1 To 10
If includenumbers = Include.Both OrElse I Mod 2 = includenumbers Mod 2 Then
TextBox2.AppendText(I.ToString)
End If
Next
End Sub
You would call it like this:
PrintNumbers(Include.Odd)

Related

my compare statement not working in vb.net

I was doing an exam algorithm earlier (Just started vb 5 days ago so be nice ;) ) and I realised after testing that for whatever reason my statement If 0 < CInt(Num(x)) < 10 was not working which completely baffled me as I would thinkk that of all things THIS would be the easiest but no. No matter what I did, it just would always be true even if I made Num(X) = 90 or 9000000 it wouldn't matter, so if anyone knows what obvious mistake I have made whether its because I made it a string or what let me know :)
Dim Num(2), INPUT As String
Dim Cond As Boolean = False
Dim Valid As Boolean = False
Dim Isnum As Boolean
Dim x, Number, IsTen As Integer
While Cond = False
Do Until IsTen = 3 And Number = 3
IsTen = 0
Number = 0
For x = 0 To 2
MsgBox("Please enter a number")
Num(x) = (Console.ReadLine)
Isnum = IsNumeric(Num(x))
If Isnum = True Then
IsTen = IsTen + 1
End If
If Isnum = True Then
If 0 < CInt(Num(x)) < 10 Then
Number = Number + 1
End If
End If
Next
If Number <> 3 Then
MsgBox("Sorry all three numbers werent between 0 and 10, Please enter again")
End If
If IsTen <> 3 Then
MsgBox("Sorry all three inputs werent numbers, Please enter again")
End If
Loop
MsgBox("All three numbers are valid")
End While
First thing I would advise is setting Option Strict On at the top of your code file. You can also make this the default via Visual Studio options. This will warn you about the implicit type conversion that is causing your problem.
Changing the line
If 0 < CInt(Num(x)) < 10 Then
I'm assuming the intent here is to ensure the number is greater than 1 but less than 10
To something like this:
If Isnum = True Then
Dim val As Integer = Integer.Parse(Num(x))
If (val > 0) AndAlso (val < 10) Then
Number = Number + 1
End If
End If
Notice the use of Integer.Parse() to type cast the string to Int, it's a little more robust but there is also the Integer.TryParse() which is another step up again, might be worth reading up on. Also notice AndAlso usage, this is a short circuit operator so if the First condition is false, doesn't bother evaluating the next.

(Visual Basic Console Mode) Check numbers from 1 to 7 and print certain output?

Question : Request a user to input a number from 1 to 7. If the number is 1 to 5. Output "Weekday" if 6 or 7 then "Weekend". Else show an error.
This is what I tried to do.
Imports System
Module Program
Dim num1 As Integer
Sub Main()
Console.WriteLine("Enter a number from 1 to 7")
num1 = Console.ReadLine
If num1 = 1 Or 2 Or 3 Or 4 Or 5 Then
Console.Write("It is a school day")
ElseIf num1 = 6 Or 7 Then
Console.WriteLine("It is the weekend")
Else
Console.WriteLine("Beep! error, enter number from 1 to 7")
End If
Console.ReadKey()
End Sub
End Module
I think the error is in If num1 = 1 or 2 or 3...
What is the correct way to phrase that statement?
Thanks
Your suspicions are correct. The problem is with your or syntax. The correct syntax would be
If num1 = 1 Or num1 = 2 Or num1 = 3 Or num1 = 4 Or num1 = 5 Then
Console.Write("It is a school day")
ElseIf num1 = 6 Or num1 = 7 Then
Console.WriteLine("It is the weekend")
Else
Console.WriteLine("Beep! error, enter number from 1 to 7")
End If
Of course it would be simpler if you used a Select Case statement.
Select Case num1
Case 1, 2, 3, 4, 5:
Console.Write("It is a school day")
Case 6, 7:
Console.WriteLine("It is the weekend")
Case Else
Console.WriteLine("Beep! error, enter number from 1 to 7")
End Select
Please turn on Option Strict. This is a 2 part process. First for the current project - In Solution Explorer double click My Project. Choose Compile on the left. In the Option Strict drop-down select ON. Second for future projects - Go to the Tools Menu -> Options -> Projects and Solutions -> VB Defaults. In the Option Strict drop-down select ON. This will save you from bugs at runtime.
Console.ReadLine returns a String. So, it cannot be directly assigned to an Integer. First, we check if a number was entered. If it isn't a number we can't compare it to other numbers. Integer.TryParse returns True if the input is a number and fills in the second parameter with the number.
Sub Main()
Dim input As String
Dim num1 As Integer
Do
Console.WriteLine("Enter a number from 1 to 7")
input = Console.ReadLine
Loop Until Integer.TryParse(input, num1)
Select Case num1
Case 1, 2, 3, 4, 5
Console.WriteLine("It is a school day")
Case 6, 7
Console.WriteLine("It is the weekend")
Case Else
Beep()
Console.WriteLine("Error, you didn't enter a number from 1 to 7")
End Select
Console.ReadKey()
End Sub

How to enable/disable button with a function

I have a problem with my university project
It's a little game, 6 buttons for each players and 2 players so 12 buttons
There is number in each buttons, if a player has his 6 buttons at 0, he can't play
I have try some Public Function and i'm actually working with a very simple one but i think this is not the problem
My function is here
And in my form, the problem is here, i've tried many things but i don't know how do to that ... I read my lesson and I'm searching on the internet, i have no idea ..
If possible is True you don't re-enable the button.
You can simplify things.
Public Function PeutJouer(ByVal joueur As Integer) As Boolean
Dim sum As Integer
Dim start As Integer = (joueur - 1) * 7
For i As Integer = start To start + 5
sum += tableau(i)
Next
Return sum <> 0
End Function
Then
Btn1P1.Enabled = PeutJouer(1)
Did you show all the relevant code? You are declaring Dim tableau(12) As Integer but the array is never filled with values. Probably tableau should be declared at the form level and not locally in this function. If you already have both, remove the local declaration, because it hides the one at form level. You also need to return the result from the function. I don't see this in your function.
Note that this
If x <> 0 Then
booleanVariable = True
Else
booleanVariable = True
End If
can be simplified to
booleanVariable = x <> 0
i.e., the condition is an expression yielding the Boolean result True or False already and you can use this value directly. When working with numeric values you don't write If x + y = 1 Then r = 1 Else If x + y = 2 Then r = 2 .... You simply write r = x + y.

Sorting List(Of T) stopping early

I have a custom class called SaveFile. One property it has is SaveNumber and I'm trying to sort the list by that property. I read a bunch of articles here and got it to sort properly but it's stopping early? The case in which I noticed this is with set that has 79 saves. It would sort: 1, 2, 3, 4, 5, 6, 7, 10, 11, ... 30, 8, 31, ... 70, 9, 71, .. The code I use is
saveList.Sort(Function(x, y) x.CompareTo(y))
But if I use the code:
For i = 0 To 3
saveList.Sort(Function(x, y) x.CompareTo(y))
Next
then it sorts right but it takes a very long time and one set has over 700 SaveFiles in it so it takes almost 5 minutes for my program to load. Any ideas?
This is the code that I have for my CompareTo function:
Public Function CompareTo(y As SaveFile) As Integer Implements IComparable(Of SaveFile).CompareTo
'If neither are an autosave then compare save number
If Not Me.Text.StartsWith("autosave") And Not y.Text.StartsWith("autosave") Then
If Me.SaveNumber.ToString.Length > y.SaveNumber.ToString.Length Then
Return False
ElseIf Me.SaveNumber.ToString.Length < y.SaveNumber.ToString.Length Then
Return True
End If
Return Me.SaveNumber < y.SaveNumber
'if either is an autosave
Else
'Create to comparable integers with
'a value of 4. We set the value to
'4 because it is higher than the 3
'available autosaves, making it sort
'after any autosaves if only one is
'an autosave.
Dim xComp As Integer = 4
Dim yComp As Integer = 4
'If x is an autosave then figure out
'which autosave number it is.
If Me.Text.StartsWith("autosave") Then
Select Case True
Case Me.Text.EndsWith("1")
xComp = 1
Case Me.Text.EndsWith("2")
xComp = 2
Case Me.Text.EndsWith("3")
xComp = 3
End Select
End If
'if y is an autosave then figure out
'which autosave number it Is.
If y.Text.StartsWith("autosave") Then
Select Case True
Case y.Text.EndsWith("1")
yComp = 1
Case y.Text.EndsWith("2")
yComp = 2
Case y.Text.EndsWith("3")
yComp = 3
End Select
End If
Return xComp < yComp
End If
End Function
First, asking 2 questions in one post is not a good idea because it reduces the chances someone will know the answer to both. For example, without seeing the code that loads these things or even what they are, makes it just a guess why it takes so long to load.
For the sorting, your CompareTo method is flawed. From MSDN:
Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
That is, it should return -1 (precedes), 1 (follows) or 0 (same). Yours just returns Boolean:
If Me.SaveNumber.ToString.Length > y.SaveNumber.ToString.Length Then
Return False
ElseIf Me.SaveNumber.ToString.Length < y.SaveNumber.ToString.Length Then
Return True
End If
...
Return xComp < yComp
We have no idea what type SaveNumber is, but if it is am integer, comparing the length of the string form if it is not the same as comparing the value:
' Me vs Other
Return "9".Length < "7".Length
That will return False (0) when it by value, it should return 1. Turning on Option Strict would have flagged the incorrect return type, which might have led you to the answer. It should be something like this, ignoring the "auto" logic:
Public Function CompareTo(other As SaveItem) As Integer _
Implements IComparable(Of SaveItem).CompareTo
... special handling for "auto"
If Me.SaveNumber = other.SaveNumber Then
Return 0
ElseIf Me.SaveNumber < other.SaveNumber Then
Return -1
Else
Return 1
End If
End Function
But, you may not even need that (assuming again that SaveNumber is an int):
saveItems = saveItems.OrderBy(Function(q) q.SaveNumber).ToList()
IComparable.CompareTo() might be needed in order to handle the "autosave" logic but it might be able to be handled in the OrderBy() depending on the information the class exposes.

How to not generate a stack overflow when a sub procedure calls itself?

This code generates a stack overflow. I'm aware it is caused by the procedure calling itself.
What can I do to avoid the stack overflow? Recalling the sub procedure and generating a new random number is the easiest thing to do, however it generates the overflow. The randomly generated number picks a random inventory item, then the if statement matches that number (random inventory item) with the quantity of that item from the deck inventory to make sure it isn't less than 1. If the inventory of that item is 0, the else plays and restarts the procedure, generating a new random number and doing the process all over again. In another procedure I have a function that if the deck's inventory becomes completely empty, then the discard pile replenishes the deck, making the discard pile empty, so there should never be a case where all randomly generated numbers can be associated item with a inventory of 0.
I wonder if I could somehow force the random number generator
Number = (DeckGroup(Rnd.Next(0, DeckGroup.Count)).ID)
not to generate numbers to inventory items DeckGroup(Number).QuantityInteger that are zero. By doing so I wouldn't even need to recall the function.
The random number is generated by a different branch in the same structure group.
Private Sub PlayElse()
Dim CardCheckBoxArray() As CheckBox = {CardCheckBox1, CardCheckBox2, CardCheckBox3, CardCheckBox4, CardCheckBox5}
'Reset Number Generator
Number = (DeckGroup(Rnd.Next(0, DeckGroup.Count)).ID)
Dim PlayerQuantitySubtractionInteger As Integer
For PlayerQuantitySubtractionInteger = ChecksDynamicA To ChecksDynamicB
If CardCheckBoxArray(TextBoxInteger).Checked = True And DeckGroup(Number).QuantityInteger > 0 Then
DeckGroup(Number).QuantityInteger -= 1
'Select the Player depending value of T
Select Case T
Case 0
Player1HandGroup(Number).QuantityInteger += 1
Case 1
Player1HandGroup(Number).QuantityInteger2 += 1
Case 2
Player1HandGroup(Number).QuantityInteger3 += 1
Case 3
Player1HandGroup(Number).QuantityInteger4 += 1
Case 4
Player1HandGroup(Number).QuantityInteger5 += 1
End Select
CardTypeArray(PlayerQuantitySubtractionInteger) = Player1HandGroup(Number).CardType
CardCheckBoxArray(TextBoxInteger).Text = Player1HandGroup(Number).CardNameString
NumberArray(PlayerQuantitySubtractionInteger) = Number
Else
If CardCheckBoxArray(TextBoxInteger).Checked = True And DeckGroup(Number).QuantityInteger < 0 Then
Call PlayElse()
End If
End If
Next PlayerQuantitySubtractionInteger
End Sub
You could use LINQ to weed out all the objects you never want to get first and then use the collection returned by the linq instead of your original collection.
Something like:
Private Sub PlayElse()
Dim CardCheckBoxArray() As CheckBox = {CardCheckBox1, CardCheckBox2, CardCheckBox3, CardCheckBox4, CardCheckBox5}
'Reset Number Generator
Dim temp As IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group) = From r In DeckGroup Where r.QuantityInteger > 0 Select r
If temp IsNot Nothing AndAlso temp.Any Then
Number = (temp(Rnd.Next(0, temp.Count)).ID)
' ** Edit **: This will ensure that you only got 1 object back from the LINQ which can tell you whether or not you have bad data. You *can* exclude this check but its good practice to include it.
Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = Nothing
Dim t = From r In temp Where r.ID = Number Select r
If t IsNot Nothing AndAlso t.Count = 1 Then
obj = t(0)
End If
If obj IsNot Nothing Then
Dim PlayerQuantitySubtractionInteger As Integer
For PlayerQuantitySubtractionInteger = ChecksDynamicA To ChecksDynamicB
' ** Edit **
obj.QuantityInteger -= 1
'Select the Player depending value of T
Select Case T
Case 0
Player1HandGroup(Number).QuantityInteger += 1
Case 1
Player1HandGroup(Number).QuantityInteger2 += 1
Case 2
Player1HandGroup(Number).QuantityInteger3 += 1
Case 3
Player1HandGroup(Number).QuantityInteger4 += 1
Case 4
Player1HandGroup(Number).QuantityInteger5 += 1
End Select
CardTypeArray(PlayerQuantitySubtractionInteger) = Player1HandGroup(Number).CardType
CardCheckBoxArray(TextBoxInteger).Text = Player1HandGroup(Number).CardNameString
NumberArray(PlayerQuantitySubtractionInteger) = Number
Next PlayerQuantitySubtractionInteger
End If
End If
End Sub
Pass through the list and determine only those that are valid. Then randomly pull from that set. Here is a simple version of it. You could use LINQ as well, but this should be clear enough:
Dim validDeckGroupsIndexes As New List(Of Integer)
For ndx As Integer = 0 to DeckGroup.Count - 1
If DeckGroup(ndx).QuantityInteger > 0 Then
validDeckGroupsIndexes .Add(ndx)
End If
Next ndx
Then use this:
Dim deckGroupNdx As Integer = Rnd.Next(0, validDeckGroupsIndexes.Count)
Number = DeckGroup(deckGroupNdx).ID