dwwilson66
dwwilson66

Reputation: 7074

I need a simple and elegant user validation technique for a c# console application

Coming from a procedural background, I'm running into a conceptual block while designing a menu-based console application and user input validation. My goal is to display a menu that launches other processes. I want to limit user input to 1, 2, or 3 at the menu.

In a procedural language, I would do something like this pseudocode:

10 print "Make a choice"
20 choice = [dataFromKeyboard]
30 if choice < 4 && choice > 0
40    then 10
50    else 60
60 process valid choices

and no matter what I try, I can't get that out of my head while designing an OO program. Consider (simplified to include only 3 menu items):

class Menu
{
    public static void Main(String[] args)
    {
        DisplayMenu thisdm = new DisplayMenu;
        int menuChoice = thisdm.displayMenu();
        ProcessMenu thispm = new ProcessMenu();
        thispm.processMenu(menuChoice);
    }
}

class DisplayMenu
{
    public int displayMenu()
    {
        Console.WriteLine("1 - foo3");
        Console.WriteLine("2 - foo2");
        Console.WriteLine("3 - foo3");
        Console.WriteLine("choose");
        String choice = Console.ReadLine();
        int intChoice = Convert.ToInt32(choice);
        return intChoice;
    }
}

class ProcessMenu
{
    public void processMenu(int choice)
    {
        switch(choice)       
        {
        case 1:   
            foo1();
            break;                  
        case 2:            
            foo2();
            break;
        case 3:            
            foo3();;
            break;         
        default:            
            Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");            
            break;      
        }
    }
}

So here's where I'm stuck. I just can't wrap my head around a simple and elegant way validate my user input that's from an OO rather than procedural standpoint.

Assuming I do the validation in the DisplayMenu, I would be validating after the input is read. But if it turns out to be invalid, how do I re-ask for valid input, since I've already called displayMenu method from Main?

I've been playing with while loops for about an hour, something like this:

intChoice = 0;
[print the menu]
while ((intChoice<1) || (intChoice>3))
Console.WriteLine("Please make a valid choice from the menu");
choice = Console.ReadLine();
etc.

but can't seem to find the sweet spot where I can control user input.

I suspect it's because I'm thinking to procedurally, and not object-oriented enough. Anyone have any tips or input to help me wrap my head around this?

Upvotes: 0

Views: 433

Answers (4)

Fede
Fede

Reputation: 44068

Expanding on @AlexeiLevenkov's suggestion of "turning your classes 90 degrees", I went a step further and created this example of a "Modular" console Application:

class Program
{
    static void Main(string[] args)
    {
        //Retrieve all Module types in the current Assembly.
        var moduletypes = Assembly.GetExecutingAssembly()
                                  .GetTypes()
                                  .Where(x => x.IsSubclassOf(typeof(ConsoleModule)));

        //Create an instance of each module
        var modules = moduletypes.Select(Activator.CreateInstance)
                                 .OfType<ConsoleModule>()
                                 .OrderBy(x => x.Id)
                                 .ToList();


        int SelectedOption = -1;

        while (SelectedOption != 0)
        {
            //Show Main Menu    
            Console.Clear();
            Console.WriteLine("Please Select An Option:\n");
            modules.ForEach(x => Console.WriteLine(string.Format("{0} - {1}", x.Id, x.DisplayName)));
            Console.WriteLine("0 - Exit\n");
            int.TryParse(Console.ReadLine(), out SelectedOption);

            //Find Module by Id based on user input
            var module = modules.FirstOrDefault(x => x.Id == SelectedOption);

            if (module != null)
            {
                //Execute Module
                Console.Clear();
                module.Execute();
                Console.WriteLine("Press Enter to Continue...");
                Console.ReadLine();
            }
        }
    }

ConsoleModule class:

public abstract class ConsoleModule
{
    public int Id { get; set; }

    public string DisplayName { get; set; }

    public abstract void Execute();

}

Some sample Modules:

public class EnterUserNameModule : ConsoleModule
{
    public EnterUserNameModule()
    {
        Id = 2;
        DisplayName = "User Name";
    }

    public static string UserName { get; set; }

    public override void Execute()
    {
        Console.WriteLine("Please Enter Your Name: ");
        UserName = Console.ReadLine();
    }
}

public class HelloWorldModule: ConsoleModule
{
    public HelloWorldModule()
    {
        Id = 1;
        DisplayName = "Hello, World!";
    }

    public override void Execute()
    {
        Console.WriteLine("Hello, " + (EnterUserNameModule.UserName ?? "World") + "!");
    }
}

public class SumModule: ConsoleModule
{
    public SumModule()
    {
        Id = 3;
        DisplayName = "Sum";
    }

    public override void Execute()
    {
        int number = 0;
        Console.Write("Enter A Number: ");
        if (int.TryParse(Console.ReadLine(), out number))
            Console.WriteLine("Your number plus 10 is: " + (number + 10));
        else
            Console.WriteLine("Could not read your number.");
    }
}

Result:

enter image description here

It uses a little bit of reflexion to find all types deriving from ConsoleModule in the current assembly, then shows a menu with all these options (which are actually properties in this class), and calls the Execute() method when an appropiate option is selected. Much more towards OO way of thinking.

Upvotes: 1

Nate
Nate

Reputation: 91

the code like:

 class Program
{
    static void Main(string[] args)
    {
        DisplayMenu thisdm = new DisplayMenu();
        ProcessMenu thispm = new ProcessMenu();

        thisdm.displayMenu();
        int menuChoice = thispm.GetChoice();
        thispm.processMenu(menuChoice);

        Console.Read();
    }

}

class DisplayMenu
{
    public void displayMenu()
    {
        Console.WriteLine("1 - foo3");
        Console.WriteLine("2 - foo2");
        Console.WriteLine("3 - foo3");
        Console.WriteLine("choose");

    }
}

class ProcessMenu
{
    public int GetChoice()
    {
        String choice = Console.ReadLine();
        int intChoice = Convert.ToInt32(choice);

        while (!Validate(intChoice))
        {
            Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");
            choice = Console.ReadLine();
            intChoice = Convert.ToInt32(choice);
        }

        return intChoice;
    }

    public void processMenu(int choice)
    {

        switch (choice)
        {
            case 1:
                //foo1();
                break;
            case 2:
                //foo2();
                break;
            case 3:
                //foo3(); ;
                break;
            default:
                //Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");
                break;
        }
    }

    private int[] forChoices=new int[]{1,2,3};

    private  bool  Validate(int choice)
    {
        if(forChoices.Contains(choice))
        {
            return true;
        }

        return false;
    }
}

Upvotes: 0

KbManu
KbManu

Reputation: 418

The way you are doing should be changed. Anyhow, for the same as your question, this works out:

DisplayMenu thisdm = new DisplayMenu();

int menuChoice = -1;

while (menuChoice < 1 || menuChoice > 3)
{
    Console.WriteLine("enter valid choice");
    menuChoice = thisdm.displayMenu();
}

ProcessMenu thispm = new ProcessMenu();
thispm.processMenu(menuChoice);

Upvotes: 0

paddy
paddy

Reputation: 63491

Make your processMenu function return some kind of indicator. You could use exceptions for this instead, but that's overkill.

public bool processMenu(int choice)
{
    ....
}

If the choice was acceptable, then return true, otherwise return false. Then:

public static void Main(String[] args)
{
    DisplayMenu thisdm = new DisplayMenu;
    ProcessMenu thispm = new ProcessMenu();

    int menuChoice;

    do {
        menuChoice = thisdm.displayMenu();
    } while( !thispm.processMenu(menuChoice) );
}

Upvotes: 0

Related Questions