Daniel Tan
Daniel Tan

Reputation: 29

Visual Basic Validation

I'm new to Visual Basics, and I tried implementing input validation for the Mark input in my application where the input can't be empty or < 0 or > 100 or isnumeric(Mark) = False or else an error message would be displayed. However, I tried leaving the input as empty or entering alphabetical values but it returned me a run time error instead. Could anyone assist me in some explanations as well as how to overcome this? Apologies if this is a dumb question.

Note: The data type for the variable "Mark" is set as single. Thank you for your time!

Sub InputMark()
     4: For I = 0 To 3
        Console.WriteLine("Please enter test marks for test " & I + 1)
        Mark(I) = Console.ReadLine

        While Mark(I) = Nothing Or Mark(I) < 0 Or Mark(I) > 100 Or IsNumeric(Mark(I)) = False
            Console.WriteLine("Please enter valid test marks.")
            GoTo 4
        End While
    Next
End Sub

Upvotes: 0

Views: 1082

Answers (3)

djv
djv

Reputation: 15782

I'm going to break this into smaller functions.

First, here is a function to process a string, which returns either a number if it satisfies your conditions, or Single.NaN.

Private Function validateMarkOrNaN(input As String) As Single
    Return If(Single.TryParse(input, validateMarkOrNaN) AndAlso 0 <= validateMarkOrNaN AndAlso validateMarkOrNaN <= 100, validateMarkOrNaN, Single.NaN)
End Function

Here is a second function which calls the first when it needs to process the user input. This function handles the flow of a single input. It can be called any number of times, requiring that you pass in the index only so it can be typed in the console.

Private Function getMarkInput(index As Integer) As Single
    Dim result As Single
    Do
        Console.WriteLine($"Please enter test marks for test {index + 1}")
        result = validateMarkOrNaN(Console.ReadLine())
        If Single.IsNaN(result) Then Console.WriteLine("Please enter valid test marks.")
    Loop While Single.IsNaN(result)
    Return result
End Function

Your main program dictates the overall flow. Here you can declare how many marks will be read, and loop over your array, getting each input into each element.

Sub Main()
    Dim numberOfMarks = 4
    Dim Mark(numberOfMarks - 1) As Single
    For i = 0 To numberOfMarks - 1
        Mark(i) = getMarkInput(i)
    Next
End Sub

Similar to your code, this will loop indefinitely as long as the user doesn't enter a valid float.

Upvotes: 1

Joel Coehoorn
Joel Coehoorn

Reputation: 416179

The data type for the variable "Mark" is set as single.

One of the nice things about strongly-typed platforms like .Net is most of your validation rules are already enforced. You will not be able to assign a non-number value to Mark, and as a value-type it can't even be null (VB.Net's Nothing will evaluate to 0 for a Single type).

So what we need to do instead is back up, and find where the data is received from the user, before it's assigned to the Mark variable. Somewhere you'll have code like Mark(I) = Console.Readline(), or similar. That's where your testing needs to be. Check the string value received from the user before assigning it to the Single value. And when you come to that point, the best way to accomplish this testing is with the Single.TryParse() function. Then, you can verify the parsed Single value is within the correct range.


Okay, with more complete code we can start to give you some real improvements.

One suggestion is to think in terms of returning a value. It's better design to let this function return the array, rather than communicating through a global variable. I mention that here because it will also mean changing how you call this function.

That out of the way:

Function InputMarks() As Single()
     Dim result(3) As Single

     For I As Integer = 0 To 3
        Dim ErrorMsg As String = ""
        Do 
            Console.Write($"{ErrorMsg}Please enter test marks for test {I+1}: ")
            Dim input As String = Console.ReadLine()
            ErrorMsg = $"{vbCrLf}Please enter valid test marks.{vbCrLf}{vbCrLf}"
        While Not Single.TryParse(input, result(I)) OrElse result(I) < 0 OrElse result(I) > 100
     Next

    Return result
End Function

No GOTO required or wanted.

If you haven't seen them yet, the $"strings" are using string interpolation and the OrElse operator instead of Or is to get short-circuiting behavior. Modern VB.Net should use OrElse and AndAlso instead of Or and And pretty much all the time.

In this case, using OrElse means we're sure Single.TryParse() completed successfully and we will have a valid number before attempting to verify the range. If the parse operation failed, the range tests won't even be attempted.

Upvotes: 2

David
David

Reputation: 6131

A couple of things:

  1. Turn option strict on: https://github.com/dday9/vb.net-tutorial/blob/master/Section%201/Chapter%201.md
  2. Using Single.TryParse on the result of ReadLine.
  3. ABANDON GOTO'! In this case a simple Do loop will suffice.

Take a look at this example:

Option Strict On
Option Explicit On

Imports System

Public Module Module1
    Public Sub Main()
        ' Declare an array of Single values
        Dim marks(3) As Single

        ' Loop from 0 to n (in this case 3)
        For counter As Integer = 0 To marks.Length - 1
            ' Declare a variable to check if the conversion was succesful
            Dim success As Boolean = False

            ' Start a Do/Until loop
            Do
                ' Propt the user to enter something
                Console.WriteLine("Please enter test marks for test " & counter + 1)

                ' Check if the conversion from ReadLine to a Single is valid
                success = Single.TryParse(Console.ReadLine(), marks(counter)) AndAlso marks(counter) >= 0 AndAlso marks(counter) <= 100

                ' If not then scold the user
                If (Not success) Then
                    Console.WriteLine("Please enter valid test marks.")
                End If
            Loop Until success
        Next

        ' Debug - just print out the values of marks
        For Each mark As Single In marks
            Console.WriteLine(mark)
        Next
    End Sub
End Module

Live Demo: https://dotnetfiddle.net/guoAzP

Upvotes: 2

Related Questions