1
\$\begingroup\$

I was searching on how to read a single character from the console and I found many irrelevant or overcomplicated posts, so I tried the following code by myself and it seems to work. But is that a proper way to read a character in terms of coding standards? I am a C# beginner.

Console.WriteLine("Please select: y/n?");
input = Console.ReadLine();
if (input == "y")
{
  Console.WriteLine("Input is: {0}", input);
}

I also tried Convert.ToChar(input) == 'y' but throws an exception when a string is given instead of a character.

Edit: I am now using ch = Console.ReadKey(true).KeyChar; to read a character from the keyboard, but still the question remains given the context within the code is being used.

That's the complete code to be reviewed:

The program class:

class Program
{
    static void Main(string[] args)
    {
        string menuSelection;

        // Instantiate the shapes.
        Circle circle = new Circle();
        Rectangle rectangle = new Rectangle();

        Console.WriteLine("Welcome, select an action from the menu below.\nType zero (0) to exit.");
        do
        {
            // Display menu.
            Console.WriteLine("\n1 Circle\n2 Rectangle\n0 Exit");
            menuSelection = Console.ReadLine();

            switch (menuSelection)
            {
                case "1": // Circle.
                    // The Circle exists.
                    if (circle.Radius > 0)
                    {
                        circle.GetProperties();
                        UserInput.ChangeProperties(circle);
                    }
                    // Create the Circle for the first time.
                    else
                    {
                        Console.WriteLine("Please input the Circle's radius_");
                        circle.Radius = UserInput.AssertIsDouble();
                    }
                    break;
                case "2": // Rectangle.
                    if (rectangle.Width > 0 && rectangle.Length > 0)
                    {
                        rectangle.GetProperties();
                        UserInput.ChangeProperties(rectangle);
                    }
                    else
                    {
                        Console.WriteLine("Please input the Rectangle's length_");
                        rectangle.Length = UserInput.AssertIsDouble();
                        Console.WriteLine("Please input the Rectangle's width_");
                        rectangle.Width = UserInput.AssertIsDouble();
                    }
                    break;
                case "0": // Exit.
                    break;
                default:
                    Console.WriteLine("\nSorry, I only accept numbers from zero (0) to six (2).");
                    break;
            }
        } while (menuSelection != "0");
    }
}

The UserInput class:

class UserInput
{
    // Valdates the users's input.
    public static double AssertIsDouble()
    {
        bool isDouble = false;
        double number = 0;

        do
        {
            if (Double.TryParse(Console.ReadLine(), out number) && number > 0)
            {
                isDouble = true;
            }
            else
            {
                Console.WriteLine("Please, input a number greater than zero (0)_");
            }

        } while (!isDouble);

        return number;
    }

    // Changes the properties of the objects at users will.
    public static void ChangeProperties(Object shape)
    {
        char ch;
        string className = shape.GetType().Name;

        Console.WriteLine("Do you want to change the properties of the {0}, y/n?", className);
        ch = Console.ReadKey(true).KeyChar; // Do not echo the user's input.
        if (ch == 'y')
        {
            switch (className)
            {
                case "Circle":
                    Circle c = (Circle)shape;
                    Console.WriteLine("Please input the Circle's new radius_");
                    c.Radius = UserInput.AssertIsDouble();
                    c.GetProperties();
                    break;
                case "Rectangle":
                    Rectangle r = (Rectangle)shape;
                    Console.WriteLine("Please input the Rectangle's new length_");
                    r.Length = UserInput.AssertIsDouble();
                    Console.WriteLine("Please input the Rectangle's new width_");
                    r.Width = UserInput.AssertIsDouble();
                    r.GetProperties();
                    break;
                default:
                    Console.WriteLine("No properties changed.\n");
                    break;
            }
        }
    }
}

The Circle class:

class Circle : Shape
{
    // Circle's radius.
    private double radius;

    // Radius property.
    public double Radius
    {
        get { return radius; }
        set { radius = value; }
    }

    // Create a Circle at will.
    public Circle()
    {
        this.radius = Radius;
        Console.Write("Circle has been created. radius is: {0}\n", radius);
    }

    // Get Circle's area.
    public override double GetArea()
    {
        return Math.PI * (radius * radius);
    }

    // Get Circle's perimeter.
    public override double GetPerimeter()
    {
        return 2 * Math.PI * radius;
    }

    // Get Circle's properties.
    public override void GetProperties()
    {
        Console.WriteLine("\nThe current Circle has the following properties:");
        Console.WriteLine("Radius: {0}\nArea: {1}\nCircumference: {2}", Radius, GetArea(), GetPerimeter());
        UserInput.Separator();
    }
}

The Rectangle class:

class Rectangle : Shape
{
    private double width; // The width field.
    private double length; // The length field.

    // The Length property.
    public double Length
    {
        get { return length; }
        set
        {
            if (value > 0)
            {
                length = value;
            }
        }
    }

    // The Width property.
    public double Width
    {
        get { return width; }
        set
        {
            if (value > 0)
            {
                width = value;
            }
        }
    }

    // Create a Rectangle at will.
    public Rectangle()
    {
        this.length = Length;
        this.width = Width;
    }

    // Create a predefined Rectangle.
    public Rectangle(double length, double width)
    {
        this.length = length;
        this.width = width;
    }

    // Get Rectangle's area.
    public override double GetArea()
    {
        return this.Length * this.Width;
    }

    // Get Rectangle's perimeter.
    public override double GetPerimeter()
    {
        return 2 * (this.Length + this.Width);
    }

    // Get Rectangle's properties.
    public override void GetProperties()
    {
        Console.WriteLine("\nThe current Rectangle has the following properties:"); 
        Console.WriteLine("Length: {0}\nWidth: {1}\nArea: {2}\nPerimeter: {3}", Length, Width, GetArea(), GetPerimeter());
        UserInput.Separator();
    }
}

The shape class:

abstract class Shape
    {
        // Shape's Methods
        abstract public double GetArea();
        abstract public double GetPerimeter();
        abstract public void GetProperties();
    }
\$\endgroup\$
4
  • 1
    \$\begingroup\$ That's very little to chew on, and whether or not that's a good way to go about it greatly depends on the purpose and context of the code. Submitting a basic "guess the number" console game would be a good way to get the most out of your Code Review experience. \$\endgroup\$ Apr 6, 2017 at 18:36
  • \$\begingroup\$ @Mat'sMug Just edited my questions, I am looking forward to your feedback. \$\endgroup\$
    – mchar
    Apr 13, 2017 at 9:25
  • \$\begingroup\$ @mchar Considering that there already are four answers to your initial question, I fear you'll need to open a new question, since this edit invalidates these answers. Most likely your edit will be rolled back. \$\endgroup\$
    – BCdotWEB
    Apr 13, 2017 at 12:07
  • \$\begingroup\$ @BCdotWEB I am afraid so, but which edit would be a proper one so not to invalidate the answers while providing more info about the problem? Or which would be the guidelines so to edit the question efficiently? It would be pity to see the question deleted along with the answerers' effort. \$\endgroup\$
    – mchar
    Apr 13, 2017 at 13:10

3 Answers 3

3
\$\begingroup\$

If you want to read a Char from the console you should use Console.ReadKey() and retrieve the KeyChar property of the returned ConsoleKeyInfo like so

Console.WriteLine("Please select: y/n?");
char input = Console.ReadKey().KeyChar;
if (input == 'y')
{
  Console.WriteLine("Input is: {0}", input);
}  

As you see the y is enclosed in single quotes which is how a char is represented.

You should consider to check for 'Y' as well because sometimes the caps lock is activated and your user would wonder why the entered keystroke hasn't been recognized.

\$\endgroup\$
4
\$\begingroup\$
Console.WriteLine("Please select (y/n)?");
while (true)
{
    char x = Char.ToLower(Console.ReadKey(true).KeyChar);
    if (x == 'y' || x == 'n')
    {
        Console.WriteLine($"Input is {x}");
        break;
    }
}

You seem to want to not echo the keypress to the output immediately (so you can output 'input is...' afterwards), so I chose to use Console.ReadKey(true) instead of Console.Read() which always echoes the char to the output. Console.ReadKey() returns a ConsoleKeyInfo struct. ConsoleKeyInfo.KeyChar contains the actual char. Note that I'm using Char.ToLower() to make sure that the returned char is lower case regardless of whether CapsLock is on or shift is held down.

However, this will return on any keypress, so I used a while loop to keep repeating the key test until you get back one of 'y' or 'n'. The purpose of the break keyword is to jump out of the loop which would otherwise be infinite: some people might think that is bad form, so you could use a bool flag or something similar. But for my money this is quite expressive of your intent.

(Also, in case you don't recognise it, I'm using the $ prefix on the output string so you can use direct string interpolation for {x}. It's a handy shortcut that more people should use.)

\$\endgroup\$
2
  • \$\begingroup\$ Very detailed answer of yours. I've got the following error, so I am referencing it to its relevant post: Unexpected character '$' String interpolation doesn't work with .NET Framework 4.6. \$\endgroup\$
    – mchar
    Apr 7, 2017 at 10:14
  • \$\begingroup\$ The string interpolation is my preferred for the C# 6.0 or newer application. Rather than having to check "...{N}...",..,..,something,.. and make sure I have all variables ordered correctly, I could just put variable ".....{something}...." as is. It be would less painful than string concatenation "...." + something + "..." as well \$\endgroup\$
    – frostshoxx
    Mar 19, 2019 at 20:41
2
\$\begingroup\$

If you only want a single character you can use Console.Read() instead of Console.ReadLine(). Unfortunately it returns as an int so you'll have to Convert it to a char, like so:

Console.WriteLine("Please select: y/n?");
int input = Console.Read();
char selection = input >= 0 ? Convert.ToChar(input) : '';
if (selection == "y")
{
    Console.WriteLine("Input is: {0}", selection);
}

The input >= 0 part is there because, according to the MSDN, Console.Read returns "negative one (-1) if there are currently no more characters to be read"

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.