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

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.

Related

Beginner to Array c#

I don't really understand arrays and I need to create a variable of type 'array of songs' then initialize it to a new Array so it can store 4 references to Songs. How would I then create a loop that would run enough times to fill the array whilst calling the InputSOngDetails() method and store the return value in that method?
namespace Songs
{
class Program
{
static void Main(string[] args) {
InputSongDetails();
}
static Song InputSongDetails()
{
Console.WriteLine("What is the name of your song");
string name = Console.ReadLine();
Console.WriteLine("What is the artists name");
string artist = Console.ReadLine();
int records;
Console.WriteLine("How many records did it sell");
while (!int.TryParse(Console.ReadLine(), out records) || records < 0)
{
Console.WriteLine("That is not valid please enter a number");
}
return new Song(name, artist, records);
}
}
}
This is my Songs class if needed
namespace Songs
{
class Song
{
string name;
string artist;
int copiesSold;
public Song(string name, string artist, int copiesSold)
{
this.name = name;
this.artist = artist;
this.copiesSold = copiesSold;
}
public Song()
{
}
public string GetArtist()
{
return artist;
}
public string GetDetails()
{
return $"Name: {name} Artist: {artist} Copies Sold: {copiesSold},";
}
public string GetCertification()
{
if (copiesSold<200000)
{
return null;
}
if (copiesSold<400000)
{
return "Silver";
}
if (copiesSold<600000)
{
return "gold";
}
return "Platinum";
}
}
}
Fist, initialize your array of songs with new Song[ length ], then a simple for-loop will suffice.
static void Main(string[] args)
{
Song[] songs = new Song[4];
for(int i = 0; i < songs.Length; i++)
{
songs[i] = InputSongDetails();
}
}
Or as the commenters suggest, just use a variable-length List<Song>.
static void Main(string[] args)
{
List<Song> songs = new List<Song>();
for(int i = 0; i < 4; i++)
{
songs.Add(InputSongDetails());
}
}
Once you've mastered the basics, you can also accomplish this with a bit of Linq (though I wouldn't actually recommend it in this case):
static void Main(string[] args)
{
var songs = Enumerable.Range(0, 4)
.Select(i => InputSongDetails())
.ToList();
}
This is not really an answer as much as it is a tip for getting input from the user in a console application which might be useful to you (well, the answer is in the last code snippet, but p.s.w.g has already covered that very well).
Since an interactive console session usually ends up with a lot of Console.WriteLine("Ask the user a question"); string input = Console.ReadLine();, and, as you've already done very well, include some validation on the input in some cases, I've found it handy to write the following methods below.
Each of them take in a string, which is the prompt for the user (the question), and return a strongly-typed variable that represents their input. Validation (when needed) is all done in a loop in the method (as you've done):
private static ConsoleKeyInfo GetKeyFromUser(string prompt)
{
Console.Write(prompt);
var key = Console.ReadKey();
Console.WriteLine();
return key;
}
private static string GetStringFromUser(string prompt)
{
Console.Write(prompt);
return Console.ReadLine();
}
public static int GetIntFromUser(string prompt = null)
{
int input;
int row = Console.CursorTop;
int promptLength = prompt?.Length ?? 0;
do
{
Console.SetCursorPosition(0, row);
Console.Write(prompt + new string(' ', Console.WindowWidth - promptLength - 1));
Console.CursorLeft = promptLength;
} while (!int.TryParse(Console.ReadLine(), out input));
return input;
}
With these methods in place, getting input is as simple as:
string name = GetStringFromUser("Enter your name: ");
int age = GetIntFromUser("Enter your age: ");
And it makes writing the method to get a Song from the user that much easier:
private static Song GetSongFromUser()
{
return new Song(
GetStringFromUser("Enter song name: "),
GetStringFromUser("Enter Artist name: "),
GetIntFromUser("Enter number of copies sold: "));
}
So now our main method just looks like (and this is the answer to your question):
private static void Main()
{
var songs = new Song[4];
for (int i = 0; i < songs.Length; i++)
{
songs[i] = GetSongFromUser();
}
Console.WriteLine("\nYou've entered the following songs: ");
foreach (Song song in songs)
{
Console.WriteLine(song.GetDetails());
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Additionally, here are some suggestions for improving the Song class.

How to test methods involving strings in 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.

C# Constructors and Contexes

I am trying to create a console application that converts centimeters to meters
Here are my objectives
Store number of centimeters entered in an attribute
Use a default constructor to store zero in the attribute that stores the number of centimeters meters entered
Use a primary constructor to accept and store number of centimeters entered.
A function call getMeters to return the number of meters
A function called Remainder to get the number of centimeter remanding after conversion
A function called Printout that will display the results
The application should carry on accepting values for conversion until the user decides to end it.
What I have so far:
using System;
namespace Conv
{
public class Centimeter
{
public int cmvar;
public Centimeter()
{
cmvar = 0;
}
}
//primary const to be added
public class MeterToCenti
{
public static void Main()
{
char choice;
char n = 'n';
do
{
Console.WriteLine("Do you want to continue? (y/n)");
//choice = Console.ReadLine();
choice = Console.ReadKey().KeyChar;
Centimeter c = new Centimeter();
Console.WriteLine("enter value in centimeters");
c.cmvar = int.Parse(Console.ReadLine());
Printout();
}
while(choice.CompareTo(n) == 0);
}
public static void getMeterst()
{
int meters = c.cmvar / 100;
}
public static void Remainder ()
{
int cmremain = c.cmvar % 100;
}
public static void Printout()
{
Console.WriteLine("{0} Meters and {1} Centimeters", meters, cmremain);
}
}
}
I am getting errors regarding:
prog.cs(24,5): warning CS0168: The variable `meters' is declared but never used
prog.cs(41,11): error CS0103: The name `c' does not exist in the current context
prog.cs(41,2): error CS0103: The name `meters' does not exist in the current context
prog.cs(47,24): error CS0103: The name `c' does not exist in the current context
prog.cs(53,61): error CS0103: The name `meters' does not exist in the current context
prog.cs(53,69): error CS0103: The name `cmremain' does not exist in the current contex
Any help would be appreciated.
In some programming languages, a context is usually defined by { and }, meaning that given this:
{
int a = ...
}
a exists specifically within that block. Assuming that no other variable named a has been declared outside the braces, doing something like so:
{
int a = ...
}
print(a)
Will result in a fault, since a no longer exists.
In your case for instance, you are declaring the following variable: Centimeter c = new Centimeter();. Notice that this is enclosed within the do...while scope, so it exists only in there. Thus, when you try to access your variable from another method, which has its own scope, you get the exception you are getting.
To start solving the issue, you should move the 3 methods getMeterst, Remainder and Printout in their appropriate class, which is Centimeter.
I would recommend you start by looking at some tutorials, since you have other issues with your code.
As pointed out by #user2864740, different languages treat scopes differently. Taking Javascript in consideration:
function hello()
{
{
var i = 44;
}
{
alert(i);
}
}
Yields an alert with the value of 44.
However, the code below does not compile in C#:
private static void Hello()
{
{
int i = 0;
}
{
Console.WriteLine(i); //The name i does not exist in the current context.
}
}
you've got a lot of issues with scope in your code, when you declare something, it can (as a rule) only be accessed inside whatever set of brackets you declare it in, so when you try and access c and cmremainand things like that without specifying their location or accessing them correctly you get errors like this.
I have working code below, but feel free to ask any extra questions as to 'why' this works.
using System;
namespace Conv
{
public class Centimeter
{
public int cmvar;
public Centimeter()
{
cmvar = 0;
}
}
//primary const to be added
public class MeterToCenti
{
public static void Main()
{
char choice;
char n = 'n';
do
{
Centimeter c = new Centimeter();
Console.WriteLine("enter value in centimeters");
c.cmvar = int.Parse(Console.ReadLine());
Printout(c);
Console.WriteLine("Do you want to continue? (y/n)");
choice = Console.ReadKey().KeyChar;
Console.WriteLine();
}
while (choice != n);
}
public static int getMeters(Centimeter c)
{
int meters = c.cmvar / 100;
return meters;
}
public static int Remainder(Centimeter c)
{
int cmremain = c.cmvar % 100;
return cmremain;
}
public static void Printout(Centimeter c)
{
Console.WriteLine("{0} Meters and {1} Centimeters", getMeters(c), Remainder(c));
}
}
}
you need to learn oriented object programming before programming any oop language.
Basic is good but polute your mind by not thinking object, but sequencial...
Here is your code fixed
using System;
namespace Conv
{
public class Centimeter
{
public int cmvar;
public Centimeter()
{
cmvar = 0;
}
public Centimeter(int mm)
{
cmvar = mm;
}
public int getMeterst()
{
return cmvar / 100;
}
public int Remainder()
{
return cmvar % 100;
}
public void Printout()
{
Console.WriteLine("{0} Meters and {1} Centimeters", this.getMeterst(), this.Remainder());
}
}
public class MeterToCenti
{
public static void Main()
{
char choice;
char n = 'n';
do
{
Console.WriteLine("Do you want to continue? (y/n)");
choice = Console.ReadKey().KeyChar;
Console.WriteLine(); // for pure design needs
Centimeter c = new Centimeter();
Console.WriteLine("enter value in centimeters");
c.cmvar = int.Parse(Console.ReadLine());
c.Printout();
}
while (choice != n);
}
}
}

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;
}
}

Can you use an array for a Switch(case)?

I've started a Uni course and I can't get me head around how to use my array for a switch case. Basically I just need the help with the switch-case, then I can get on with my work.
Heres what it looks like so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace Assignment2
{
class Program
{
public const int noOfentries = 6;
public const int address = 5;
public static string[,] addressBook = new string[noOfentries, address];//
string array for the address book
public static int deletion;
public static int choice;
public static ConsoleKeyInfo keyPressed;
public static short curItem = 0, c;
public static string[,] menuItems = new string[,]
{
{"Add Entry"},
{"Delete Entry"},
{"Print Book to Screen"},
{"Edit Contact"},
{"Exit"}
};
#region addEntry
#endregion
#region deleteEntry
#endregion
#region seeBook
#endregion
public static void fourthChoice()
{
Console.WriteLine("Would you like to edit the name or address?");
}
public static void menu()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(menuItems[i,0].PadRight(10));
Console.Clear();
for (c = 0; c < menuItems.Length; c++)
{
if (curItem == c)
{
Console.Write(">");
Console.WriteLine(menuItems[c,0]);
Console.ForegroundColor = ConsoleColor.Green;
}
else
{
Console.WriteLine(menuItems[c,0]);
}
}
Console.WriteLine("Please select an option with the Arrow Keys");
}
}
public static void entries()
{
switch (menuItems[0,0])
{
case "Add Entry":
break;
case "Delete Entry":
break;
case "Print Book to Screen":
break;
case "Edit Contact":
break;
case "Exit":
break;
}
}
There are two aspects to a switch/case:
The value you're switching on
The cases you want to branch to
The fact that you're getting the value from an array is irrelevant. It's equivalent to:
string value = menuItems[0, 0];
switch (value)
{
}
Your cases are also constant string values, so that's fine too. (There's duplication there leading to fragile code which you may want to address, but that's a separate matter.)
... and that's fine. It's not really clear what problem you're having at the moment, although it's also unclear why you've got a rectangular array at all, given that you've only got a single "column" per row. Why not just:
public static string[] menuItems = new string[]
{
"Add Entry",
"Delete Entry",
"Print Book to Screen",
"Edit Contact",
"Exit"
};
(Leaving aside naming, accessibility, mutability etc.)
You should use an Enum for that:
private Enum myEnum { Add, Delete, Edit }
void main(myEnum state)
{
switch (state)
{
Add: //do things
break;
Edit: //do things
break;
Delete: //do things
break;
}
}

Categories