In this C# programming tutorial, we’re going to show you how to build your first C# app: a calculator.
Not only will these techniques help you practice both the C# programming fundamentals and C# programming logic, but give you crucial experience and a basic understanding in using one of the top 5 programming languages in the entire world!
We’ll be using the in-browser IDE ( relpit.com ) which requires no setup, so if you’re ready, let’s build our app!
About C# Programming
C# is a popular programming language developed by Microsoft. It is an object oriented language based off of the C programming language, so it features many of the same common language infrastructure features. Additionally, it was built on top of Microsoft’s popular Net framework, giving you access to many useful features via the System namespace.
While there are many other popular programming languages out there, C# programming is unique in its balance of efficiency with human readability. It has seen wide use in developing video games, desktop applications, and even mobile applications.
For this tutorial, we already expect you know some C# programming basics such as local variables, primitive types, loops, and so forth. We also assume you’re familiar with using class libraries, in particular the Console class. While we’ll be using Replit, as mentioned above, you are also welcome to follow along with Visual Studio. That being said, we do encourage you to make sure you at least know how to build a simple “Hello World” app first to be assured Visual Studio will run as intended.
Sprint 1 – Pseudocode
Sometimes when you have to perform a programming task that will require more than a couple of steps it can seem a bit overwhelming. It is often helpful to break your task down into simple next steps. These are not all the steps to complete your task but high-level next steps that can at least simulate creating the task result you want.
For example, your task may involve getting data from the user:
- In our initial iteration, we can note this but choose to provide a “hard-coded” value to simply get our logic working.
- In the next iteration, we can return to implement logic to get actual user input. At this point, we can enter a value as if we’re the user. Since we know what our app is expecting we enter the value correctly. For example, when our “Calculator App” expects a number we enter a number; when it expects a math operator we enter “+”.
- In the next iteration, we can choose to handle the situation where the user does not enter the correct information. We can choose how to validate the input and what our program flow should do if the information entered is invalid.
- In the next iteration, we can choose to expand our error tracking to unexpected errors, called “Exceptions” in C# programming, and introduce logic to both “catch” the error as well as logic on what our app should do when an exception occurs.
As you can see, even with this simple example and a few simple observations, programming can get overwhelming quickly if you try to code all possible features, optimize your code, and protect your app all in the first pass.
Let’s try this approach with our simple C# programming calculator app.
using System; class MainClass { public static void Main (string[] args) { // Display App Name as "Simple C# Calculator" // Get first number (in 10) // Get math operator (in +) // Get second number (in 2) // Perform calculation to get the result (out 12) // Determine if user wants to do more calculations (conditional check) // true (if true; most steps repeated) // set first value to last result // get next math operator // get second number // perform calculation to get new result // Determine if user wants to do more calculations // false (if false) // exit Calculator app loop // Display result to user (out "Total: 12") } }
Notice that in just this first pass of pseudocode we can see the opportunity to use several key C# programming fundamentals:
- Variables to hold data being tracked and changed
- Decision Control based on boolean conditions
- Loops to repeat code logic
- Methods to consolidate code for repeating tasks
- Classes and Objects to encapsulate state and related actions
I know this is a lot but let’s start slow in the next iteration by gradually replacing some pseudocode with actual code. You’ll also notice the need to add additional comments (TODO items) as well as additional pseudocode as we make progress towards our finished C# programming Calculator App.
Sprint 2 – Basic Working Code
As we begin to replace pseudocode with actual C# programming code, run your program often to make sure no C# compiler errors are being introduced. This is known as “doing things right”.
As we get blocks of code completed we can expand our testing to include logic, making sure our functionality is correct. This is known as “doing the right things”.
using System; class MainClass { public static void Main (string[] args) { Console.Clear(); // Display App Name as "Simple C# Calculator" Console.WriteLine("Simple C# Calculator"); // Get first number (in 10) double firstNum = 10; // Get math operator (in +) string mathOp = "+"; // Get second number (in 2) double secondNum = 2; // Perform calculation to get result (out 12) double result = firstNum + secondNum; // Determine if user wants to do more calculations (conditional check; assume FALSE) // true (if true; most steps repeated) // set first value to last result // get next math operator // get second number // perform calculation to get new result // Determine if user wants to do more calculations // false (if false) // exit Calculator app loop // Display result to user (out "Total: 12") Console.WriteLine("Answer: {0} {1} {2} = {3}", firstNum, mathOp, secondNum, result); Console.WriteLine("Answer: "+firstNum+" "+mathOp+" "+secondNum+" = "+result); } }
The two Console.WriteLine() options produce the same result. The first method is based on substitution and will obey any spaces or other text inside the double-quotes. The numbers in the substitution syntax {0} correspond to the variable order in the comma-separated list that follows the WriteLine() string. In the second, simple concatenation example, you need to add any spaces you want as double-quotes ” “ with space in-between. Moving forward with this example we’ll use the substitution syntax.
Simple C# Calculator Answer: 10 + 2 = 12 Answer: 10 + 2 = 12
Sprint 3 – Code While Loop
We can see from the pseudocode, that we need to repeat what we present to the user if the user wants to continue. This lends itself to putting our repeating code inside a Loop with a conditional check that allows us to exit the loop when the user is finished.
This leaves us with two important design decisions:
- What do we want to do on each iteration? This code will go inside the loop.
- How do we want the user to indicate they are finished? This must feed into our loop condition.
There are many ways to implement this logic. For example, if the user leaves the “Math Operator” empty we can exit:
- Let’s print this instruction for the user.
- Let’s code a while loop to test this option.
using System; class MainClass { public static void Main (string[] args) { // Clear the Console before we start displaying calculations Console.Clear(); // Display App Name as "Simple C# Calculator" Console.WriteLine("Simple C# Calculator"); Console.WriteLine("Math Operators: [ + | - | * | / ] leave empty to exit"); // Declare variables we need for Calculator double firstNum, secondNum, result; string mathOp = "+"; // Get first number before loop (in 10) firstNum = 10; // TODO: remove logic to exit test loop int testCounter = 1; /* Determine if user wants to do more calculations based on whether or not the math operators question is left blank (String.Empty in C# programming) */ // true - repeat most steps // false - exit Calculator app loop while (mathOp != String.Empty) { // TODO: remove logic to exit test loop if (testCounter < 5) { Console.WriteLine("Iteration: {0}", testCounter); testCounter++; } else { mathOp = String.Empty; continue; } // Get math operator (in +) mathOp = "+"; // Get second number (in 2) secondNum = testCounter; // Perform calculation to get result result = firstNum + secondNum; // Display result to the user Console.WriteLine("Answer: {0} {1} {2} = {3}",firstNum, mathOp, secondNum, result); // set the first number to be the last result before the loop begins again firstNum = result; } } }
For test purposes, we have introduced a temporary testCounter which we then use inside the loop to display the current iteration, increment its value, use to simulate more dynamic input from the user and finally set to our while loop exit condition after four test iterations. This simulates the user adding together five numbers before leaving mathOp empty to simulate being done with calculations. The simulation now looks like this inside our Console:
Simple C# programming Calculator Math Operators: [ + | - | * | / ] leave empty to exit Iteration: 1 Answer: 10 + 2 = 12 Iteration: 2 Answer: 12 + 3 = 15 Iteration: 3 Answer: 15 + 4 = 19 Iteration: 4 Answer: 19 + 5 = 24
Sprint 4 – User Input – Numbers
With a basic working data structure of our C# programming Calculator App in place, it’s time to make it real by adding user input. In this next step, we will accept user input and incorporate it into our functionality with no validation or error handling. Our goal is to simply accept user input in place of our current “hard-coded” values for numbers. We will leave the mathOp “hard-coded” to + for this iteration.
The key C# programming Console command we will work with is Console.ReadLine(). To this point in the tutorial, we have focused on printing information to the Console. In this Sprint, we do the opposite by retrieving data the user types into the Console in response to our app’s prompts for information; namely firstNum and secondNum (we will handle mathOp input and processing in the next Sprint).
While Console.ReadLine() will give us access to what the user types in the Conole, it’s important to note that all its input is returned as a String data type (even when we ask for a number and our app can only calculate numbers). For this reason, our app will need to first convert the input strings to Numbers before attempting any calculations. One way to do this is using C# programming’s static Convert class to access its Convert.ToDouble() method which takes the String input from the Console and converts it to a double data-type before storing its, now Number data-type, value in our firstNum variable. We’ll repeat this for the secondNum variable. After firstNum is provided by the user initially, its value is automatically updated by our code to be the previous result.
using System; class MainClass { public static void Main (string[] args) { Console.Clear(); Console.WriteLine("Simple C# Calculator"); Console.WriteLine("Math Operators: [ + | - | * | / ] leave empty to exit"); // Declare variables we need for Calculator double firstNum, secondNum, result; string mathOp; // Get first number before loop Console.Write("Enter first number: "); firstNum = Convert.ToDouble(Console.ReadLine()); do { // Get math operator (in +) mathOp = "+"; Console.Write("{0} + __ : ", firstNum); try { secondNum = Convert.ToDouble(Console.ReadLine()); } catch { mathOp = String.Empty; continue; } // Perform calculation to get result result = firstNum + secondNum; // Display result to the user Console.WriteLine("{0} {1} {2} = {3}",firstNum, mathOp, secondNum, result); // Assign result to firstNum before looping again firstNum = result; } while (mathOp != String.Empty); Console.WriteLine("Answer: {0}", result); } }
Notice that we use Console.Write() to prompt the user for a number instead of Console.WriteLine() we’ve been using so far. The key difference is that WriteLine() automatically takes the user to a new line in the Console whereas Write() waits for user input on the same line.
Notice that we use a Try-Catch Block to handle the C# programming exception that will be thrown if the user enters a non-numeric character or leaves it blank. In this scenario, we assume that entering a non-numeric value indicates the calculations have finished so we set the Loop condition exit criteria to true and use the keyword continue to take the code straight to the Loop Condition; which is now false. The loop ends and the final line of the app is printed providing the final answer.
Sprint 5 – User Input – Operator
We can add together as many numbers as we want. The next challenge is to give the user the option to do more than just add numbers.
We will use the same Console.ReadLine() as we did for the firstNum and secondNum in the last section to get the user input. As we know, ReadLine() returns a String. The difference is that for mathOp, we want a String so no conversion is necessary.
What we do need to do is to determine what mathOp symbol was provided. This tells us if we should add, multiple, subtract or divide the firstNum and secondNum.
While we can do this with an IF statement, it’s actually a great use-case for C# programming’s SWITCH Statement which is great when evaluating a selection of known values associated with a single variable.
We’ll use switch to check for +, -, *, / and make the appropriate calculation. If not one of these symbols then we again set the loop exit condition to be false and use the keyword continue to take the code straight to the Loop Condition; which is now false. The loop ends and the final line of the app is printed providing the final answer.
using System; class MainClass { public static void Main (string[] args) { Console.Clear(); Console.WriteLine("Simple C# Calculator"); Console.WriteLine("Math Operators: [ + | - | * | / ] leave empty to exit"); // Declare variables we need for Calculator double firstNum, secondNum, result = 0; string mathOp = "+"; string[] ValidMathOperators = {"+","-","*","/"}; // Get first number before loop Console.Write("Enter first number: "); firstNum = Convert.ToDouble(Console.ReadLine()); while (mathOp != String.Empty) { try { // Get math operator (input from user) Console.Write("Enter math operator: "); mathOp = Console.ReadLine(); // check if math operator entered by user is valid; if not exit loop if (!Array.Exists(ValidMathOperators, e => e == mathOp)) { Console.WriteLine("Invalid Math Operator"); mathOp = String.Empty; continue; } // Write the first part of the equation Console.Write("{0} {1} ", firstNum, mathOp); // Get second number (input from user) secondNum = Convert.ToDouble(Console.ReadLine()); } catch { Console.WriteLine("Does Not Compute!"); mathOp = String.Empty; continue; } // determine which math operator the user entered switch (mathOp) { case "+": result = firstNum + secondNum; Console.WriteLine("Add: {0}", result); break; case "-": result = firstNum - secondNum; Console.WriteLine("Subtract: {0}", result); break; case "*": result = firstNum * secondNum; Console.WriteLine("Multiply: {0}", result); break; case "/": result = firstNum / secondNum; Console.WriteLine("Divide: {0}", result); break; default: // error with math operator so exit calculation loop mathOp = String.Empty; continue; } // Display result to the user Console.WriteLine(" = {0}", result); // Assign result to firstNum before looping again firstNum = result; } Console.WriteLine("Answer: {0}", result); } }
We now have a working, interactive C# programming Calculator. Here is an example, displayed step by step, in the Console:
Simple C# Calculator Math Operators: [ + | - | * | / ] leave empty to exit Enter first number: 7 Enter math operator: + 7 + 7 = 14 Enter math operator: * 14 * 2 = 28 Enter math operator: - 28 - 8 = 20 Enter math operator: / 20 / 5 = 4 Enter math operator: Invalid Math Operator Answer: 4
Sprint 6 – Classes and Methods
We now have a working, interactive C# programming Console Calculator but we have a lot of code in the Main method. We can improve our calculator, by making its implementation cleaner and more reusable by moving some of the logic into Classes and Methods.
C# is an Object-Oriented programming language. Object-Oriented Programming (OOP) is a major subject in itself so this will just be a high-level overview of what you need to know to begin using Classes and Methods in C# programming. There are four pillars of OOP and we’ll touch on the first two in this Sprint. The four pillars are:
- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
The idea behind OOP is to model “real-world” objects using Classes! In our case, we will use a C# programming class as a “blueprint” for a “real-world” calculator. By doing this we “abstract” the internal workings of our calculator, let’s say the “logic board”, from the outside display and user interaction. The logic board’s specific data (Properties) and functionality (Methods) are now “encapsulated” inside a C# programming class which hides its inner workings (how it performs calculations) from the outside world. What goes into the Calculator class depends on how we want to model the calculator. If we say the inside of the calculator handles memory, calculation and validation then we could use the Main method to hold the calculator’s UI and keyboard interactions with the user.
Following this idea let’s create a Calculator class and move the following functionality into it:
- Memory – variables to store numbers and math operator
- Conversion – converting input into useable numbers
- Validation – ensuring the math operator entered is a valid math symbol
- Calculation – based on the first and second number together with math operator
- Finished – determines when the user is finished calculating numbers
Keep in the Main method:
- Display Welcome and Calculator information to the user
- Start Calculator by creating a Calculator Object
- WriteLine() – displaying results to the user
- ReadLine() – getting information from the user
- App Loop – considered to be the user interacting with the calculator
- Exit – observes the finished indicator from the Calculator class and stops the app from running when ContinueCalculating is set to false
using System; class MainClass { public static void Main (string[] args) { // Clear Console and Welcome User Console.Clear(); Console.WriteLine("Simple C# Calculator"); Console.WriteLine("Math Operators: [ + | - | * | / ]"); Calculator calc = new Calculator(); // Get the first number before loop starts Console.Write("Enter first number: "); calc.SetNumber(Console.ReadLine()); while (calc.ContinueCalculating) { // Get math operator (input from user) Console.Write("[ + | - | * | / ] "); calc.MathOp = Console.ReadLine(); // Get second number (input from user) Console.Write("{0} {1} ", calc.FirstNum, calc.MathOp); calc.SetNumber(Console.ReadLine()); if (calc.ContinueCalculating) { // Calculate Result calc.Calculate(); // Display Result to the User Console.WriteLine(" = {0}", calc.Result); } } Console.WriteLine("Result: {0}", calc.Result); } } // --- Calculator Class --- (could be in separate file) class Calculator { // Declare private variables private string[] validMathOperators = {"+","-","*","/"}; private string prevMathOp, mathOp; private bool firstNumber; // Declare Calculator Properties // (read value from anywhere; set value only in this class) public bool ContinueCalculating {get; private set;} public double FirstNum {get; private set;} public double SecondNum {get; private set;} public double Result {get; private set;} public string MathOp { get { return mathOp;} set { if (Array.Exists(validMathOperators, el => el == value)) { mathOp = value; } else { mathOp = prevMathOp; } } } // Class Constructor public Calculator() { // Initialize Properties and Variables when Calculator Object is created prevMathOp = validMathOperators[0]; firstNumber = true; ContinueCalculating = true; } // public Methods public void SetNumber (string input) { double number; try { number = Convert.ToDouble(input); } catch { number = 0; ContinueCalculating = false; } if (firstNumber) { FirstNum = number; firstNumber = false; } else { SecondNum = number; } } public void Calculate() { // Perform Calculation based on FirstNum, MathOp, SecondNum switch (MathOp) { case "+": Result = FirstNum + SecondNum; break; case "-": Result = FirstNum - SecondNum; break; case "*": Result = FirstNum * SecondNum; break; case "/": Result = FirstNum / SecondNum; break; default: break; } /* We continue calculating from the previous number... We accomplish this by assigning the current Result to the FirstNum property once the current calculation is complete. */ FirstNum = Result; /* We want the math operator to default to its previous value if the user leaves it blank... We accomplish this by using a previous math operator variable and assigning the current math operator value once the current calculation is complete. */ prevMathOp = MathOp; } }
We now have a working, interactive C# programming Console Calculator. Here is an example, displayed step by step, in the Console:
Simple C# Calculator Math Operators: [ + | - | * | / ] Enter first number: 5 [ + | - | * | / ] 5 + 6 = 11 [ + | - | * | / ] 11 + 7 = 18 [ + | - | * | / ] * 18 * 4 = 72 [ + | - | * | / ] / 72 / 9 = 8 [ + | - | * | / ] 8 / Result: 8
Notice, in the above code, that in our Calculator class Properties we use Property Getters and Setters. This allows you to control access to your class. In this example, we allow anyone outside our class to read and write to the MathOp Property but we perform validation in the Setter before any value is actually assigned in our class. If the validation is true, we assign the value from the user. If the validation is false, we assign the previous math operator value instead.
For our Numbers ( FirstNum and SecondNum ) Properties we only allow those values to be set within the Calculator class. We can do this by making the Property’s Setter private. Now the value is updated by using a class Method which provides us with the opportunity to validate user input, convert input into the number format we need and control what happens when the user enters nothing at all.
Finally, we use a Calculator class property in the Main method app loop to evaluate the exit condition. Inside the Calculator class, we change this exit condition to be an empty value for secondNum and implement it by setting the class Property ContinueCalculating to false. This value is then read by the Main method and directs the code to break out of the app loop. This causes a final message to print to the user and then ends the program.
Sprint 7 – Refactor App using C# Inheritance
In the last section, we abstracted the internal workings of our calculator into its own class thereby cleaning up the Main method of the application considerably. At present, the Main method still contains all the display and user interaction logic which is a lot of code.
Let’s say we want the Main method to simply handle our app’s code execution and not directly hold any logic related to the calculator. In designing this solution we could also consider future development. What does this mean?
At the moment we are developing a calculator in the Console. Perhaps in the future, we may develop a web calculator or a desktop calculator. While this would alter the “exterior”, such as display and user interaction, the “internal” workings of the calculator could remain the same. We can create a new ConsoleCalculator class without changing anything about our existing Calculator class yet have access to all of its functionality. We do this by using another Object-Oriented Programming (OOP) practice, known as “inheritance”. In the code below, notice that only the Main method where the object ( calc ) is created uses this object reference. All the logic that has been refactored out of Main no longer uses the object reference since it is now inside a different class.
using System; class MainClass { public static void Main (string[] args) { ConsoleCalculator calc = new ConsoleCalculator(); calc.On(); calc.GetFirstNumber(); while (calc.ContinueCalculating) { calc.GetMathOperator(); calc.GetSecondNumber(); calc.Calculate(); } calc.Off(); } } /* --- ConsoleCalculator Class --- (could be in separate file) ConsoleCalculator inherits from its base class Calculator using the following syntax: */ class ConsoleCalculator : Calculator { public void On() { // Clear Console and Welcome User Console.Clear(); Console.WriteLine("Simple C# Calculator"); Console.WriteLine("Math Operators: [ + | - | * | / ]"); } public void Off() { Console.WriteLine("Result: {0}", Result); } public void GetFirstNumber() { // Get the first number before loop starts Console.Write("Enter first number: "); SetNumber(Console.ReadLine()); } public void GetMathOperator() { // Get math operator (input from user) Console.Write("[ + | - | * | / ] "); MathOp = Console.ReadLine(); } public void GetSecondNumber() { // Get second number (input from user) Console.Write("{0} {1} ", FirstNum, MathOp); SetNumber(Console.ReadLine()); } public void Calculate() { if (ContinueCalculating) { // Calculate Result base.Calculate(); // Display Result to the User Console.WriteLine(" = {0}", Result); } } } // --- Calculator Class --- (could be in separate file) // Existing Code - no changes required
In the code above, we refactor the Main method to move all of the Console Calculator’s display and user interaction logic into the ConsoleCalculator class:
- Display Welcome and Calculator information to the user
- WriteLine() – displaying results to the user
- ReadLine() – getting information from the user
This now represents the “exterior” of our calculator. Since we still need access to our “internal” logic we have ConsoleCalculator “inherit” from Calculator using the following :Â syntax:
class ConsoleCalculator : Calculator { // ConsoleCalculator code }
The new ConsoleCalculator class is known as the “derived” class and the Calculator class is known as the “base” class. Inheritance allows us to directly access all the Properties ( such as Result, FirstNum, and MathOp ) and Methods ( such as SetNumber() ) of the base class (Calculator). We also have a special case in the method named Calculate() since it appears in both classes. This enables the derived class to implement behaviour specific to the ConsoleCalculator while also executing the functionality contained in the base class. Since the default within the ConsoleCalculator, or any object created from it (calc in the Main method), is to call the ConsoleCalculator‘s method we tell our C# programming to execute the base class method using the base keyword as follows:
public void Calculate() { if (ContinueCalculating) { // Calculate Result using base class base.Calculate(); // Display Result to the User Console.WriteLine(" = {0}", Result); } }
We have now successfully abstracted and encapsulated the internal and external workings of our calculator into their own classes. Also, since we inherited the internal workings from the base Calculator class (created in the previous Sprint) we can reuse this base class code to create other types of calculators, such as web or desktop, in the future. We also have a very clean Main method that handles only our app’s high-level execution:
- Start Calculator by creating a Calculator Object
- App Loop – considered to be the user interacting with the calculator
- Exit – observes the finished indicator from the Calculator class and stops the app from running when ContinueCalculating is set to false
Conclusion
Congratulations! You have just coded a working, object-oriented C# programming calculator app from scratch!!!
Over this C# programming tutorial, we’ve covered a lot. We’ve learned how to deal with user input, utilize computer calculations, and address topics like inheritance. Most importantly here, though, we’ve learned how to take a base concept and slowly build upon it until we were able to create a clean-looking code base for our app to run on.
Of course, this is just the start. C# is a general-purpose language, and it can do a lot – even beyond simple Console applications. Software, video games, and so much more are available to you, which is why C# is one of the most popular programming languages. Even not considering projects themselves, there are many functional programming techniques to explore with C# not covered here, such as: making user defined types, setting up static methods, using multiple interfaces, and so forth.
So get out there and start making your own apps!
BUILD GAMES
FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.