How to test methods involving strings in C# - c#

I have an app which allows you to add student and lecturer details, and search them, and display them, etc. This is for a college assignment, and I have to test five of the methods I have created. Firstly, I'm not sure how to test a method involving strings, as all the testing methods I have seen involved a bank account app, and testing withdrawal and deposit methods seems easy as you just have to add and subtract numbers. I'm not sure at all how to test my, (for example) AddLecturer() method. I've tried to get one of the methods to throw an exception if a Status class that I created is entered correctly, but the program seems to still consider it an unhandled exception. How do I fix the exception so it's handled correctly, and how do I test these other methods?
Here's the main entry point to the app with all the methods.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DBSManagement
{
public class College: Staff
{
public static List<Student> students = new List<Student>();
public static List<Lecturer> lecturers = new List<Lecturer>();
public static void Main()
{
int choice;
bool seeAgain = true;
do
{
Console.WriteLine("Press");
Console.WriteLine("1: To add a student");
Console.WriteLine("2: To add a lecturer");
Console.WriteLine("3: To search for a lecturer or student");
Console.WriteLine("4: To show the details of all enrolled students");
Console.WriteLine("5: To show the names of all lecturers");
Console.WriteLine("6: To show payroll details for a lecturer");
Console.WriteLine("7: To quit");
int.TryParse(Console.ReadLine(), out choice);
switch (choice)
{
case 1:
AddStudent();
break;
case 2:
AddLecturer();
break;
case 3:
SearchPerson();
break;
case 4:
ShowStudents();
break;
case 5:
ShowLecturers();
break;
case 6:
ShowPayrollDetails();
break;
case 7:
seeAgain = false;
break;
default:
Console.WriteLine("Invalid option selected");
break;
}
} while (seeAgain);
}
public static void AddStudent()
{
Student student = new Student();
Console.WriteLine("Enter student name:");
if (Console.ReadLine() != null)
{
student.Name = Console.ReadLine();
}
else throw new ArgumentNullException("Please enter a name");
Console.WriteLine("Enter student address:");
student.Address = Console.ReadLine();
Console.WriteLine("Enter student phone number:");
student.Phone = Console.ReadLine();
Console.WriteLine("Enter student email:");
student.Email = Console.ReadLine();
Console.WriteLine("Enter student PPSN:");
student.PPSN = Console.ReadLine();
Console.WriteLine("Enter student status (postgrad or undergrad):");
EnterStat:
string stat = Console.ReadLine().ToLower();
if (stat == "postgrad" || stat == "undergrad")
{
student.Status = (Status)Enum.Parse(typeof(Status), stat);
}
else
{
Console.WriteLine("Please enter either postgrad or undergrad:");
goto EnterStat;
}
Console.WriteLine("Enter student ID:");
int inStudentID;
int.TryParse(Console.ReadLine(), out inStudentID);
student.StudentID = inStudentID;
students.Add(student);
}
public static void AddLecturer()
{
Lecturer lecturer = new Lecturer();
Console.WriteLine("Enter lecturer name:");
lecturer.Name = Console.ReadLine();
Console.WriteLine("Enter lecturer address:");
lecturer.Address = Console.ReadLine();
Console.WriteLine("Enter lecturer phone number:");
lecturer.Phone = Console.ReadLine();
Console.WriteLine("Enter lecturer email:");
lecturer.Email = Console.ReadLine();
Console.WriteLine("Enter lecturer PPSN:");
lecturer.PPSN = Console.ReadLine();
Console.WriteLine("Enter lecturer ID:");
lecturer.ID = Console.ReadLine();
Console.WriteLine("Enter salary:");
lecturer.Salary = decimal.Parse(Console.ReadLine());
Console.WriteLine("Enter subject taught:");
lecturer.SubjectTaught = Console.ReadLine().ToLower();
lecturers.Add(lecturer);
}
public static void SearchPerson()
{
int searchChoice = 0;
int studentSearch = 0;
int lecturerSearch = 0;
Console.WriteLine("Press:");
Console.WriteLine("1 to search for a student");
Console.WriteLine("2 to search for a lecturer");
int.TryParse(Console.ReadLine(), out searchChoice);
switch (searchChoice)
{
//search students
case 1:
Console.WriteLine("Press:");
Console.WriteLine("1 to search by name");
Console.WriteLine("2 to search by student number");
int.TryParse(Console.ReadLine(), out studentSearch);
switch (studentSearch)
{
case 1:
Console.WriteLine("Enter student name:");
string studentNameSearch = Console.ReadLine();
bool sFound = false;
foreach (Student student in students)
{
if (student.Name.Contains(studentNameSearch))
{
Console.WriteLine(student.ToString());
sFound = true;
break;
}
}
if (sFound == false)
{
Console.WriteLine("Student name not found");
}
break;
case 2:
int studentIDSearch;
bool IDFound = false;
Console.WriteLine("Enter student number:");
int.TryParse(Console.ReadLine(), out studentIDSearch);
foreach (Student student in students)
{
if (student.StudentID.Equals(studentIDSearch))
{
Console.WriteLine(student.ToString());
IDFound = true;
break;
}
}
if (IDFound == false)
{
Console.WriteLine("Student name not found");
}
break;
default:
Console.WriteLine("Invalid option selected");
break;
}
break;
//search lecturers
case 2:
Console.WriteLine("Press:");
Console.WriteLine("1 to search by name");
Console.WriteLine("2 to search by course taught");
int.TryParse(Console.ReadLine(), out lecturerSearch);
switch (lecturerSearch)
{
case 1:
Console.WriteLine("Enter lecturer name:");
string lecturerNameSearch = Console.ReadLine();
bool lFound = false;
foreach (Lecturer lecturer in lecturers)
{
if (lecturer.Name.Contains(lecturerNameSearch))
{
Console.WriteLine(lecturer.ToString());
lFound = true;
break;
}
}
if (lFound == false)
{
Console.WriteLine("Lecturer name not found");
}
break;
case 2:
Console.WriteLine("Enter course taught:");
string lecturerSubjectSearch = Console.ReadLine().ToLower();
bool subjectFound = false;
foreach (Lecturer lecturer in lecturers)
{
if (lecturer.SubjectTaught.Contains(lecturerSubjectSearch))
{
Console.WriteLine(lecturer.ToString());
subjectFound = true;
break;
}
}
if (subjectFound == false)
{
Console.WriteLine("Subject not found");
}
break;
default:
Console.WriteLine("Invalid option selected");
break;
}
break;
default:
Console.WriteLine("Invalid option selected");
break;
}
}
public static void ShowStudents()
{
//sort list by name
List<Student> SortedStudents = students.OrderBy(o => o.Name).ToList();
foreach (Student student in SortedStudents)
{
Console.WriteLine(student);
}
}
public static void ShowLecturers()
{
//sort list by name
List<Lecturer> SortedLecturers = lecturers.OrderBy(o => o.Name).ToList();
foreach (Lecturer lecturer in SortedLecturers)
{
Console.WriteLine(lecturer.Name);
}
}
public static void ShowPayrollDetails()
{
Console.WriteLine("Enter lecturer name:");
string lecturerNameSearch = Console.ReadLine();
for (int i = 0; i < lecturers.Count; i++)
{
if (lecturers[i].Name == lecturerNameSearch)
{
Console.WriteLine(lecturers[i].PayrollDetails());
}
else
{
Console.WriteLine("Lecturer name not found");
}
}
}
}
}
Here are the test methods I have created so far.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DBSManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DBSManagement.Tests
{
[TestClass()]
public class CollegeTests
{
[TestMethod()]
[ExpectedException(typeof(ArgumentException))]
public void AddStudentTest()
{
//arrange
string s = "student";
Status status = (Status)Enum.Parse(typeof(Status), s);
//act
Student student1 = new Student("Name", "123 Fake St", "0851234567", "fake#address.com", "7895459R", status, 12345678);
//assert
//handled by exception
}
[TestMethod()]
public void AddLecturerTest()
{
Assert.Fail();
}
[TestMethod()]
public void SearchPersonTest()
{
Assert.Fail();
}
[TestMethod()]
public void ShowStudentsTest()
{
Assert.Fail();
}
[TestMethod()]
public void ShowLecturersTest()
{
Assert.Fail();
}
[TestMethod()]
public void ShowPayrollDetailsTest()
{
Assert.
This is the student class. I'm attempting to make it throw an exception if anyone enters a status other than postgrad or undergrad. These are enums.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DBSManagement
{
public class Student : Person
{
private Status status;
//auto-implemented properties
public Status Status
{
get
{
return status;
}
set
{
if (value == Status.undergrad || value == Status.postgrad)
{
status = value;
}
else throw new ArgumentException("Error: please select undergrad or postgrad");
}
}
public int StudentID { get; set; }
//empty constructor
public Student() { }
//constructor with parameters
public Student(string name, string address, string phone, string email, string ppsn, Status status, int studentId)
:base(name, address, phone, email, ppsn)
{
Status = status;
StudentID = studentId;
}
//overridden ToString() method
public override string ToString()
{
return string.Format("Name: {0}\nStudent Number: {1}\nAddress: {2}\nPhone: {3}\nEmail: {4}\nStatus: {5}",
Name, StudentID, Address, Phone, Email, Status);
}
}
}

You can test your code, but these tests will be very fragile (and, as #Scott Chamberlain noted, it won't be clear what they will be proving).
What you need to do is to "hide" that ugly Console.ReadLine() behind something you have "programmatic" control over. Func<string> would be ideal:
public static void AddStudent(Func<string> consoleReader)
{
Student student = new Student();
Console.WriteLine("Enter student name:");
student.Name = Console.ReadLine();
// ...
}
With this, your tests become something like:
[Test]
void TestAddStudent()
{
var n = 0;
var strings = new[] {
"Name",
"123 Fake St",
"0851234567",
"fake#address.com",
"7895459R",
// ...
};
Func<string> consoleReader = () => strings[n++];
var student = AddStudent(consoleReader);
Assert.AreEqual(strings[0], student.Name);
// ...
}

If you want to do testing it would be easier if you separate your UI from the logic. You could for instance adopt a MVC pattern or something like that. Start by building all your data objects like Lecturer, Student, etc. These objects would be your data model. Then add the logic, or controls, which manipulate these data objects. There could be a AddLecturer(..) method in the control component. Finally make a UI, or view, which interacts with them without being fully intertwined like in your code. With regard to testing, you'll mainly be writing tests for methods in the control components and maybe the model. There are plenty of things to test. Take your add lecturer method:
Is the name longer than 3 characters?
Is there at least two names? (Maybe this is a too strong assumption?)
Is the phone number correctly formatted?
Is the e-mail correctly formatted?
Is the lecturer ID only numbers? (Although, I'd expect the lecturer ID is something generated by your system)
Is the PPSN well formatted?
Is the salary positive?
Is the salary under a ludicrously large amount?
Is the salary even a number?`
When adding a new lecturer to lecturers did it really get added? (Typically you'd never check this. We trust the basic collections, unless you wrote it yourself.)
etc.

Related

How to search element in LinkedList with custom object

I need to find a node with input from a customer in the linked list but I have an error CS1503. How can I solve a problem?
In this code, I create the LinkList name "customerList" to collect string data such as name, contact number, and payment from the user. After that, I need to find the contact number which input from the user to show data in the node and delete it. In code show that input in "searchCustomerDetail" cannot convert 'string' to ....
Error Message: Argument 1: cannot convert from 'string' to 'IFN564.Customer' [IFN564]csharp(CS1503)
public class Customer {
public string Name { get; set; }
public string Phone { get; set; }
public string Payment { get; set; }
public int[] Screening { get; set; }
public static LinkedList<Customer> customerList = new LinkedList<Customer>();
public static string input;
public static void addCustomerDetail() {
Console.WriteLine("Please enter your information detail");
Console.WriteLine("");
Console.Write("Full Name: ");
string inputName = Console.ReadLine();
Console.Write("Contact Number: ");
string inputPhone = Console.ReadLine();
Console.Write("Payment Method: ");
string inputPayment = Console.ReadLine();
Console.Clear();
Console.WriteLine("");
Console.WriteLine("Please check your information detail!!");
Console.WriteLine("");
Console.WriteLine($"Full Name: {inputName}");
Console.WriteLine($"Contact Number: {inputPhone}");
Console.WriteLine($"Payment Method: {inputPayment}");
Console.WriteLine("");
Console.WriteLine("Please 1 to confirm or 0 to cancel");
int input = Convert.ToInt32(Console.ReadLine());
switch (input) {
case 1:
insert(inputName, inputPhone, inputPayment);
break;
case 2:
Program.Main();
break;
}
}
public static void insert(string name, string phone, string payment) {
Console.WriteLine("");
Console.WriteLine("Please 1 to confirm buy ticket or 0 to cancel");
int input = Convert.ToInt32(Console.ReadLine());
Customer customerDetail = new Customer() {
Name = name,
Phone = phone,
Payment = payment,
};
switch (input) {
case 0: Program.Main(); break;
case 1:
customerList.AddLast(customerDetail);
Program.Main();
break;
}
}
public static void searchCunstomerDetail() {
Console.WriteLine("Please enter contact number!!");
Console.WriteLine("");
Console.Write("Contact number: ");
input = Console.ReadLine();
LinkedListNode<Customer> node = customerList.Find(input);
Console.WriteLine(node);
}
}
I try to use LinkListNode to find but It show error with input CS1503
you need to use LINQ where
var node = customerList.Where(c=>c.Phone == input).First();
The error is with this statement:
LinkedListNode<Customer> node = customerList.Find(input);
customerList.Find searches the customerList for an element (in this case, a Customer) that equals the argument provided to Find (in this case, the var, input). input is a string, not Customer, so it can't be used in Find here.
What you seemingly intend to do here is to find the Customer in customerList that has a Customer.Phone that equals the input string. To do so, you can use LINQ, as others have suggested:
LinkedListNode<Customer> node = customerList.FirstOrDefault(c => c.Phone == input);
The above expression will find the first element in the list that returns true for the provided function (predicate), and will return null if none match.

How to add option to save app state in console application?

I am using a constructor Employee(string id, string fullname, string roleid) in the business logic to add elements in the list Employees. The business logic I used is:
namespace ClassLibrary2
{
public class Employe
{
public List<Employee> Employees { get; set; } = new List<Employee>();
public void Add(Employee employee)
{
Employees.Add(employee);
}
public List<Employee> ViewAll()
{
return Employees;
}}}
The above business logic is executed in the below console application :
namespace EmployeeApp
{
static class Program
{
static void Main(string[] args)
{
Console.WriteLine("PPM Project");
Console.WriteLine("1. Add employee");
Console.WriteLine("2. View Employee");
Console.WriteLine("3. Save");
var userInput = Console.ReadLine();
var business1 = new Employe();
while (true)
{
switch (userInput)
{
case "1":
Console.WriteLine("adding employee full name:");
var FName = Console.ReadLine();
Console.WriteLine("adding 4digit Employee Id:");
var EmpId = Console.ReadLine();
var newEmployee = new Employee(EmpId, FName);
business1.Add(newEmployee);
Console.WriteLine($"An employee with EmployeeId {newEmployee.EmpId} was added. \n");
break;
case "2":
var allemployee = business1.ViewAll();
Console.WriteLine("\nList of Employees: \n");
foreach (var employee in allemployee)
{
Console.WriteLine($"Employee: {employee.EmpId}\t {employee.FName}.");
}
break;
case "3":
//how to save user input for further use
break;
}
Console.WriteLine("Select option");
userInput = Console.ReadLine();
}}}}
I want to save the app state so that when I restart the program I can retrieve the saved input. I tried saving the data in .txt file; using the How to permanently save input given by user in c# console application? but because I am providing user input in Lists in console rather than giving input in application, I am having trouble following the previous thread.

My C# code on Visual Studio 2013 doesn't work properly

I am trying to be an educated lazy chemistry student, by making a C# program that can do chemistry calculation for me. In order to make the code, I have to understand well the procedures in chemistry class.
I am new to any kind of programming, C# is my first language.
The code works fine for 1 Element calculation, but not 2 Elements calculation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
MassCalculation myMassCalculation = new MassCalculation();
TwoMassCalculation myTwoMassCalculation = new TwoMassCalculation();
Console.WriteLine("How many elements are in the compound?");
string userMainInput = Console.ReadLine();
if (userMainInput == "1")
{
myMassCalculation.Amount1 = 1;
Console.WriteLine("What is the ELEMENT?");
string userInput1 = Console.ReadLine();
Elements element;
if (Enum.TryParse<Elements>(userInput1, true, out element))
{
switch (element)
{
case Elements.Na:
myMassCalculation.Element1 = 22.990;
break;
case Elements.Cl:
myMassCalculation.Element1 = 35.453;
break;
default:
break;
}
}
Console.WriteLine("How many?");
string userAmount1 = Console.ReadLine();
int myAmount1 = int.Parse(userAmount1);
myMassCalculation.Amount1 = myAmount1;
myMassCalculation.DoCalculation();
resultOfMassCalculation(myMassCalculation);
}
if (userMainInput == "2")
{
Console.WriteLine("What is the First ELEMENT?");
string userInput1 = Console.ReadLine();
Elements element;
if (Enum.TryParse<Elements>(userInput1, true, out element))
{
switch (element)
{
case Elements.Na:
myMassCalculation.Element1 = 22.990;
break;
case Elements.Cl:
myMassCalculation.Element1 = 35.453;
break;
default:
break;
}
}
Console.WriteLine("How many?");
string userAmount1 = Console.ReadLine();
int myAmount1 = int.Parse(userAmount1);
myMassCalculation.Amount1 = myAmount1;
Console.WriteLine("What is the Second ELEMENT?");
string userInput2 = Console.ReadLine();
if (Enum.TryParse<Elements>(userInput2, true, out element))
{
switch (element)
{
case Elements.Na:
myTwoMassCalculation.Element2 = 22.990;
break;
case Elements.Cl:
myTwoMassCalculation.Element2 = 35.453;
break;
default:
break;
}
}
Console.WriteLine("How many?");
string userAmount2 = Console.ReadLine();
int myAmount2 = int.Parse(userAmount2);
myTwoMassCalculation.Amount2 = myAmount2;
myTwoMassCalculation.DoCalculation();
resultOfMassCalculation(myTwoMassCalculation);
}
Console.ReadLine();
}
private static void resultOfMassCalculation(MassCalculation calculation)
{
Console.Write("The Mass is {0}g/mol", calculation.DoCalculation());
}
}
enum Elements
{
Na,
Cl,
}
class MassCalculation
{
public double Element1 { get; set; }
public int Amount1 { get; set; }
public virtual double DoCalculation()
{
double result = Element1 * Amount1;
return result;
}
}
class TwoMassCalculation : MassCalculation
{
public double Element2 { get; set; }
public int Amount2 { get; set; }
public override double DoCalculation()
{
double result = Element1 * Amount1 + Element2 * Amount2;
return result;
}
}
}
Please help! I know it seems somewhat unprofessional. I have just started programming a week ago, and this is the best I can do. I need guidance.
The only elements defined in the code is Na and Cl, I am trying to calculate NaCl. When everything is in place, I will add more elements to the list, and many more different types of calculations.
I'll take constructive opinions.
Thank you so much in advance.
I refactored your code a little. It will work the same way, but wont crash on inappropriate user input
https://dotnetfiddle.net/CMQugr
using System;
using System.Collections.Generic;
namespace Test
{
public class Program
{
public static Dictionary<string, double> Elements = new Dictionary<string, double>
{
{"Na",22.990},
{"Cl",35.453}
};
public static void Main()
{
double result = 0;
int elemenCountInput;
do
{
Console.WriteLine("How many elements are in the compound?");
} while (!Int32.TryParse(Console.ReadLine(), out elemenCountInput));
for (int i = 0; i < elemenCountInput; i++)
{
string element;
do
{
Console.WriteLine("What is the {0} element", (i + 1));
element = Console.ReadLine();
} while (!Elements.ContainsKey(element));
int amount;
do
{
Console.WriteLine("How many");
} while (!Int32.TryParse(Console.ReadLine(), out amount));
result += Elements[element] * amount;
}
Console.Write("The Mass is {0}g/mol", result);
Console.ReadLine();
}
}
}
There is problem in the code when elements are two. You are assigning the first element value to "myMassCalculation' object and second element value to "myTwoMassCalculation". When you call "DoCalculation()" "myTwoMassCalculation.Element1' and "myTwoMassCalculation.Amount1" have no values. That's why it is giving wrong answer. Make the following changes and try:
if (Enum.TryParse<Elements>(userInput1, true, out element))
{
switch (element)
{
case Elements.Na:
myTwoMassCalculation.Element1 = 22.990;
break;
case Elements.Cl:
myTwoMassCalculation.Element1 = 35.453;
break;
default:
break;
}
}
Console.WriteLine("How many?");
string userAmount1 = Console.ReadLine();
int myAmount1 = int.Parse(userAmount1);
myTwoMassCalculation.Amount1 = myAmount1;
I'd do something like:
Create a class for elements (name (string), whatever that number is (double/decimal)
Create a static dictionary of them of them indexed by name.
Loop through the parameters to Main (or loop around an input command) looking up each entry in the dictionary then perform the calculation.
Convert to LINQ if desired.
This is be a great approach and should teach you a lot. I'm not going to write it for you (time and desire), but I may come back later with an example.

How to retrieve input from a function

I have the next code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Maman15cs
{
public class ClassRoom
{
public string ClassNumber;
public int NumberofPlaces;
public int[,] DayandHour = new int[6,8];
public void AddClassRoom()
{
Console.WriteLine("Enter the Class number, the Number of places\n");
ClassNumber = Console.ReadLine().ToString();
NumberofPlaces = int.Parse(Console.ReadLine());
Console.WriteLine("Good, now enter the Day(1, 2, 3, 4, 5, 6) and after that you put the courses' number that are that day (In Order)");
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 8; j++)
{
DayandHour[i,j] = int.Parse(Console.ReadLine());
}
}
}
}
public class Course
{
public string CourseName;
public int CourseNumber;
public int StudentsNumber;
public string TeacherName;
public string ClassNumber;
// Tuple<string, int, int, string, string>
public void AddCourse(Course *course)
{
Console.WriteLine("Enter the Course's name, course's number, students number, teacher's name, and class' number\n");
CourseName = Console.ReadLine().ToString();
CourseNumber = int.Parse(Console.ReadLine());
StudentsNumber = int.Parse(Console.ReadLine());
TeacherName = Console.ReadLine().ToString();
ClassNumber = Console.ReadLine().ToString();
}
}
public class Program
{
void Main()
{
Course[] course = new Course[1000];
ClassRoom[] classroom = new ClassRoom[1000];
Course* coursePointer;
int actionChoice;
int courseCount = 0, classroomCount = 0;
loop:
Console.WriteLine("What do you want to do? (Enter number): \n 1) Add a new Course \n 2)Add a new class room \n 3)Add an existing class to an existing classroom \n 4)Read the information of a specific classroom \n 5)Read the information of all the classrooms \n 6)Read the information of a specific course \n 7)Delete a specific course \n 8)Update courses in the Time Table \n 9)Exit the program \n");
actionChoice = int.Parse(Console.ReadLine());
switch (actionChoice)
{
case 1: //Add a new Course
// course[classroomCount].AddCourse();
break;
}
goto loop;
}
}
}
And I want the AddCourse function to return or use the pointer to add the input to the variable course, I tried some things like list<> but I'm not that experienced with this.
Change AddCourse to create a new Course and return it.
public Course AddCourse()
{
var course = new Course();
course.CourseName = Console.ReadLine().ToString();
// ... more readlines
return course;
}
In Main:
List<Course> courses = new List<Course>();
case 1: courses.Add(AddCourse()); break;
I had similar problems after switch to C# from C :)
First, you can replace Course[] course = new Course[1000]; with var course = new List<Course>();. List<T> is much better for the most scenaros - it has no exact size, you can add any numer of elements 'on the fly', on any position.
Second, all class instances passed as reference. Pointers are usable only in some rare scenarous.
Third. goto almost never used in C# too. There are tons of loops, enumerators etc in the language - foreach, while, for
Last. In your case I would do it in this way:
public class Course
{
public string CourseName;
public int CourseNumber;
public int StudentsNumber;
public string TeacherName;
public string ClassNumber;
public static Course ReadCourse()
{
var rez = new Course();
Console.WriteLine("Enter the Course's name, course's number, students number, teacher's name, and class' number\n");
rez.CourseName = Console.ReadLine().ToString();
rez.CourseNumber = int.Parse(Console.ReadLine());
rez.StudentsNumber = int.Parse(Console.ReadLine());
rez.TeacherName = Console.ReadLine().ToString();
rez.ClassNumber = Console.ReadLine().ToString();
return rez;
}
}
public class Program
{
void Main()
{
var courses = new List<Course>();
int actionChoice;
while(1=1)
{
Console.WriteLine("What do you want to do? (Enter number): \n 1) Add a new Course \n 2)Add a new class room \n 3)Add an existing class to an existing classroom \n 4)Read the information of a specific classroom \n 5)Read the information of all the classrooms \n 6)Read the information of a specific course \n 7)Delete a specific course \n 8)Update courses in the Time Table \n 9)Exit the program \n");
actionChoice = int.Parse(Console.ReadLine());
switch (actionChoice)
{
case 1: //Add a new Course
var new_course = Course.ReadCourse();
courses.Add(new_course);
break;
case 9: // Exit
return;
default:
Console.WriteLine("Wrong input");
}
}
}
}
What is interesting here. Static method Course.ReadCourse which reads and return new instance of Course. default selector in switch. return to exit the app. List<T> as a storage for courses. new Course() command uses implicit constructor created automatically because no any constructors were defined.
First, set up a list to hold all your courses, and not necessarily an array (unless you really need an array):
List<Course> Courses = new List<Courses>();
Change your AddCourse method return a newly instantiated Course object:
Public Course AddCourse(){
Course newCourse = new Course();
<logic to populate the object>
return newCourse;
}
Inside your loop where you're adding courses, just do something similar to this:
Courses.add(AddCourse());
Then you can use whatever looping structure to go through all the courses or linq to get a specific one you need.
---EDIT--
Since you're stuck with the way your Course class is set up (which is not best practice btw), you will need to change the AddCourse method to something like this:
public class Course
{
public string CourseName;
public int CourseNumber;
public int StudentsNumber;
public string TeacherName;
public string ClassNumber;
public void AddCourse()
{
Console.WriteLine("Enter the Course's name, course's number, students number, teacher's name, and class' number\n");
this.CourseName = Console.ReadLine().ToString();
this.CourseNumber = int.Parse(Console.ReadLine());
this.StudentsNumber = int.Parse(Console.ReadLine());
this.TeacherName = Console.ReadLine().ToString();
this.ClassNumber = Console.ReadLine().ToString();
}
}
Then the call in your looping method will need to be like this:
Course NewCourse = new Course();
Courses.Add(NewCourse.AddCourse());

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?
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:
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.
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) );
}
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);
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;
}
}

Categories