I am having trouble with this line of code right here...why am I being prompted with this error? I am getting an error saying "Operator '|' cannot be applied to operands of type 'bool' and 'string' How do check if my residency variable is not equal to these 2 strings I have listed in the if statement?
catch (ArgumentException)
{
if (age > 16 | age > 80)
{
Console.WriteLine("You can only enter states of OH or MI ad the driver's age must be between 16 and 80.");
}
if (residency != "OH" | "MI")
{
Console.WriteLine("You can only enter states of OH or MI ad the driver's age must be between 16 and 80.");
}
}
Here is the full code if you want to get a better idea of what I am trying to execute.
class Program
{
static void Main(string[] args)
{
CarInsurance create = new CarInsurance();
Console.Write("Enter the age of the driver: ");
int age = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter the state the driver lives in: ");
string residence = Convert.ToString(Console.ReadLine());
create.PremiumCalculate(age, residence);
}
}
class CarInsurance
{
public int driverAge { get; set; }
public string residency { get; set; }
public int totalPremium;
public int GetPremium()
{
return totalPremium;
}
public void PremiumCalculate(int age, string residency)
{
int premiumOhio = 100;
int premiumMichigan = 250;
try
{
if (residency == "MI")
{
int total = (100 - age) * 3 + premiumMichigan;
Console.WriteLine("Your premium is {0}", total.ToString("C"));
}
if (residency == "OH")
{
int total = (100 - age) * 3 + premiumOhio;
Console.WriteLine("Your premium is {0}", total.ToString("C"));
}
}
catch (ArgumentException)
{
if (age > 16 | age > 80)
{
Console.WriteLine("You can only enter states of OH or MI ad the driver's age must be between 16 and 80.");
}
if (residency != "OH" | "MI")
{
Console.WriteLine("You can only enter states of OH or MI ad the driver's age must be between 16 and 80.");
}
}
}
}
You should apply it between two conditions, not values. Note
if (residency != "OH" || residency != "MI")
Note, however, that this condition will always return true. You probably meant to use &&:
if (residency != "OH" && residency != "MI")
Let me explain the issue first. Here residency != "OH" | "MI", you are evaluating residency != "OH" and this works and produces bool. Then produced bool fails against | "MI" because operator | is invalid for strings.
If you had 2 strings, you would get
Operator '|' cannot be applied to operands of type 'string' and 'string'
One way to fix it is
using System.Linq;
. . ..
var exclusionList = new [] {"OH","MI"};
if (!exclusionList.Contains(residency))
How about something like this. First, I start off by listing all states (yeah, you only care about two of them, but any of them is a valid state):
public enum ResidentState
{
AL,
AK,
AZ,
AR,
CA,
CO,
CT,
DE,
FL,
GA,
HI,
ID,
IL,
IN,
IA,
KS,
KY,
LA,
ME,
MD,
MA,
MI,
MN,
MS,
MO,
MT,
NE,
NV,
NH,
NJ,
NM,
NY,
NC,
ND,
OH,
OK,
OR,
PA,
RI,
SC,
SD,
TN,
TX,
UT,
VT,
VA,
WA,
WV,
WI,
WY,
}
Then I start my car insurance class:
class CarInsurance
{
public int DriverAge { get; private set; }
public ResidentState Residency { get; private set; }
public int TotalPremium { get; private set; }
private bool _ageOk = false;
private bool _residencyOk = false;
}
It has publicly readable properties for DriverAge, ResidentState and TotalPremium. They can only be set from within the class. It also has a few bookkeeping flags for age and residency validity.
Then, I add a couple of member functions to that class to set the DriverAge and Residency. They do error checking. They both have the same pattern. They will return false unless you set them correctly:
public bool SetResidency(ResidentState residency)
{
if (residency != ResidentState.OH && residency != ResidentState.MI)
{
Console.WriteLine("You can only enter states of OH or MI");
return false;
}
Residency = residency;
_residencyOk = true;
return true;
}
public bool SetDriverAge(int age)
{
if (age < 16 || age > 80)
{
Console.WriteLine("The driver's age must between 16 and 80.");
return false;
}
_ageOk = true;
DriverAge = age;
return true;
}
If the caller is using these correctly, then _ageOk and _residencyOk will be true. They may be false if not.
Then I create the CalculatePremium function to that class (and a few constants) (note, I renamed it to be verb-noun, I think it makes more sense):
private const int PremiumOhio = 100;
private const int PremiumMichigan = 250;
public int CalculatePremium()
{
if (!_residencyOk)
{
throw new ArgumentException("The driver's residency must first be entered, and only Ohio and Michigan are valid states");
}
if (!_ageOk)
{
throw new ArgumentException("The driver's age must first be entered, and it must be between 16 and 80");
}
int premium;
switch (Residency)
{
case ResidentState.MI:
premium = PremiumMichigan;
break;
case ResidentState.OH:
premium = PremiumOhio;
break;
default:
premium = 10_000;
break;
}
TotalPremium = (100 - DriverAge) * 3 + premium;
Console.WriteLine($"Your total premium is {TotalPremium:C}");
return TotalPremium;
}
Finally, I wrote the Program class that has the Main function:
class Program
{
static void Main(string[] args)
{
CarInsurance insurance = new CarInsurance();
bool ageOk = false;
do
{
Console.Write("Enter the age of the driver: ");
var response = Console.ReadLine();
ageOk = int.TryParse(response, out var age);
if (!ageOk)
{
Console.WriteLine("You must enter an integer for age");
}
else
{
ageOk = insurance.SetDriverAge(age);
}
} while (!ageOk);
bool stateOk = false;
do
{
Console.Write("Enter the state the driver lives in: ");
var stateStr = Console.ReadLine();
stateOk = Enum.TryParse<ResidentState>(stateStr, true, out var state);
if (!stateOk)
{
Console.WriteLine($"The state you entered ({stateStr}) is not a valid two-letter state abbreviation");
}
else
{
stateOk = insurance.SetResidency(state);
}
} while (!stateOk);
var premium = insurance.CalculatePremium();
}
}
Note that I check that the entered data is a valid integer (or a valid state abbreviation) in this code. Then I check (using the Insurance class) that it is valid for this calculation. As it's written, it will not cause either of the exceptions within CalculatePremium to be thrown. However, it might make sense to wrap the call to CalculatePremium in a try/catch. I'll leave that up to you - you really need to read up on exceptions.
I wrote this is a way (and explained it in a way), that will allow you to explore some of the features of the language. Step through it in the debugger. Enter invalid entries for both quantities (something that's not an integer, and then something that's out of range for age, and something that's not a state and then not either MI or OH) and see what happens. Try calling calculate premium with invalid values and see what happens (and try to catch the result).
Related
I have created an ArgumetException catch block and it is not throwing anything when I input something it is supposed to catch...how do I fix this? For example if I input the driver age as 15 or 81 it still goes through with out being caught and it calculates the final premium. I also would enter their state of residency that isn't Ohio or Michigan and my catch block is not catching anything. I don't know if you can understand what I'm trying to say but if you copy and paste this into your IDE and try running it your self I think you'll understand it a bit more.
Here is the code
class Program
{
static void Main(string[] args)
{
CarInsurance create = new CarInsurance();
try
{
Console.Write("Enter the age of the driver: ");
int age = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter the state the driver lives in: ");
string residence = Convert.ToString(Console.ReadLine());
create.PremiumCalculate(age, residence);
}
catch (FormatException) // catches if user inputs a string instead of a value
{
Console.WriteLine("You didn't enter a numeric value for the age of the driver!");
}
}
}
class CarInsurance
{
public int driverAge { get; set; } // gets and sets driver age
public string residency { get; set; } // gets and sets residency
public int totalPremium;
public int GetPremium() // gets the premium ***readonly***
{
return totalPremium;
}
public void PremiumCalculate(int age, string residency) // main method for CarInurance class
{
int premiumOhio = 100;
int premiumMichigan = 250;
try
{
if (residency == "MI")
{
int total = (100 - age) * 3 + premiumMichigan;
Console.WriteLine("Your premium is {0}", total.ToString("C"));
}
if (residency == "OH")
{
int total = (100 - age) * 3 + premiumOhio;
Console.WriteLine("Your premium is {0}", total.ToString("C"));
}
}
catch (ArgumentException)
{
if (age < 16 && age > 80)
{
Console.WriteLine("You can only enter states of OH or MI ad the driver's age must be between 16 and 80.");
}
if (residency != "OH" && residency != "MI")
{
Console.WriteLine("You can only enter states of OH or MI ad the driver's age must be between 16 and 80.");
}
}
}
}
The final try-catch block at the bottom of the code is what I am having a problem with.
I'm trying to write line where if text (anything other than number) typed it shows error message that it is not number and ask user to type again.
Also I have implanted that number must be 20 or higher which works fine and when ever user input less than 20 it shows error message and ask user again..
so my problem is that it shows error message for everything including number less than 20 and text.
So how can I make else statement that shows different message if text is type rather than number?
static double InputFuel() {
double fFuel;
string text;
bool badValue = true;
Console.Write("Enter amount of fuel used in litres : ");
//Check if fule entered is greater than 20, if not ask again
do {
text = Console.ReadLine();
if (double.TryParse(text, out fFuel) && fFuel >= 20) {
badValue = false;
}
else {
Console.WriteLine("\n\t {0} is below the minimum value of 20 \n\n", text);
Console.Write("Please re-enter a number greater than 20 : ");
}
} while (badValue);
return fFuel;
}//end InputFuel
I tried something like this but not working
else (!int.TryParse(text, out num) {
Console.WriteLine("\n\t {0} is not a number \n\n", text);
}
Firstly I would check if input is number and after that if number is greater than 20
static double InputFuel() {
double fFuel;
string text;
bool badValue = true;
Console.Write("Enter amount of fuel used in litres : ");
//Check if fule entered is greater than 20, if not ask again
do {
text = Console.ReadLine();
if (!double.TryParse(text, out fFuel) {
Console.WriteLine("\n\t {0} is not a number \n\n", text);
}
else if (fFuel >= 20) {
badValue = false;
}
else {
Console.WriteLine("\n\t {0} is below the minimum value of 20 \n\n", text);
Console.Write("Please re-enter a number greater than 20 : ");
}
} while (badValue);
return fFuel;
}//end InputFuel
You can your modify your current condition to be nested like below
if (double.TryParse(text, out fFuel)) {
if(fFuel >= 20) badValue = false;
else {
Console.WriteLine("\n\t {0} is below the minimum value of 20 \n\n", text);
Console.Write("Please re-enter a number greater than 20 : ");
}
}
else {
Console.WriteLine("\n\t {0} is not a number \n\n", text);
}
If you're looking for 'more advanced' or 'more enterprise-like' way of validating user input you can create a struct or class that will do the validation and provide some information about it :
class InputValidator
{
public string Input { get; set; }
public bool IsValidInput { get; set; }
public bool IsAboveThreshHold { get; set; }
public bool IsNumber { get; set; }
public double Litres { get; set; }
public ValidationResult() { }
public ValidationResult(string text)
{
double litres; Input = text;
if (double.TryParse(text, out litres))
{
Litres = litres;
IsAboveThreshHold = litres > 20;
IsNumber = true;
}
IsValidInput = IsNumber && IsAboveThreshHold;
}
public void ShowErrorMessage()
{
if (!IsNumber)
{
Console.WriteLine($"\n\t {Input} is not a valid number \n\n");
Console.Write("Please re-enter a number greater than 20 : ");
return;
}
if(!IsAboveThreshHold)
{
Console.WriteLine($"\n\t {Input} is below the minimum value of 20 \n\n");
Console.Write("Please re-enter a number greater than 20 : ");
}
}
}
And use this class very easily :
static double InputFuel()
{
var result = new InputValidator();
Console.Write("Enter amount of fuel used in litres : ");
//Check if fule entered is greater than 20, if not ask again
while (!result.IsValidInput)
{
result = new InputValidator(Console.ReadLine());
if (!result.IsValidInput) result.ShowErrorMessage();
}
return result.Litres;
}
P.S.
While in simple cases this would be an overkill but in more complex cases that are common in enterprise projects this approach is much better to use.
This question already has answers here:
Why did I get the compile error "Use of unassigned local variable"?
(10 answers)
Closed 2 days ago.
I keep getting this error for annualRate, monthlyCharge, and lateFee.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Lab_5___Danny_Curro
{
class Program
{
static void Main(string[] args)
{
string firstName;
string lastName;
int accNumber;
string creditPlan;
double balance;
string status;
Boolean late = false;
double lateFee;
double monthlyCharge;
double annualRate;
double netBalance;
Console.Write("Enter First Name: ");
firstName = Console.ReadLine();
Console.Write("Enter Last Name: ");
lastName = Console.ReadLine();
Console.Write("Enter Account Number: ");
accNumber = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter Credit Card Plan Number[Blank Will Enter Plan 0]: ");
creditPlan = Console.ReadLine();
Console.Write("Enter Balance: ");
balance = Convert.ToDouble(Console.ReadLine());
Console.Write("Is This Account Late?: ");
status = Console.ReadLine().Trim().ToLower();
if (creditPlan == "0")
{
annualRate = 0.35; //35%
lateFee = 0.0;
monthlyCharge = balance * (annualRate * (1 / 12));
return;
}
if (creditPlan == "1")
{
annualRate = 0.30; //30%
if (status == "y")
{
late = true;
}
else if (status == "n")
{
late = false;
}
if (late == true)
{
lateFee = 25.00;
}
monthlyCharge = balance * (annualRate * (1 / 12));
return;
}
if (creditPlan == "2")
{
annualRate = 0.20; //20%
if (status == "y")
{
late = true;
}
else if (status == "n")
{
late = false;
}
if (late == true)
{
lateFee = 35.00;
}
if (balance > 100)
{
monthlyCharge = balance * (annualRate * (1 / 12));
}
else
{
monthlyCharge = 0;
}
return;
}
if (creditPlan == "3")
{
annualRate = 0.15; //15%
lateFee = 0.00;
if (balance > 500)
{
monthlyCharge = (balance - 500) * (annualRate * (1 / 12));
}
else
{
monthlyCharge = 0;
}
return;
}
netBalance = balance - (lateFee + monthlyCharge);
Console.WriteLine("Name: \t\t\t {0} {1}", firstName, lastName);
Console.WriteLine("Account Number: \t{0}", accNumber);
Console.WriteLine("Credit Plane: \t\t{0}",creditPlan);
Console.WriteLine("Account Late: \t\t{0}", late);
Console.WriteLine("Balance: \t\t{0}", balance);
Console.WriteLine("Late Fee: \t\t{0}", lateFee);
Console.WriteLine("Interest Charge: \t{0}", monthlyCharge);
Console.WriteLine("Net Balance: \t\t{0}",netBalance);
Console.WriteLine("Annual Rate: \t\t{0}", annualRate);
Console.ReadKey();
}
}
}
The compiler isn't smart enough to know that at least one of your if blocks will be executed. Therefore, it doesn't see that variables like annualRate will be assigned no matter what. Here's how you can make the compiler understand:
if (creditPlan == "0")
{
// ...
}
else if (creditPlan == "1")
{
// ...
}
else if (creditPlan == "2")
{
// ...
}
else
{
// ...
}
The compiler knows that with an if/else block, one of the blocks is guaranteed to be executed, and therefore if you're assigning the variable in all of the blocks, it won't give the compiler error.
By the way, you can also use a switch statement instead of ifs to maybe make your code cleaner.
Change your declarations to this:
double lateFee = 0.0;
double monthlyCharge = 0.0;
double annualRate = 0.0;
The error is caused because there is at least one path through your code where these variables end up not getting set to anything.
Because if none of the if statements evaluate to true then the local variable will be unassigned. Throw an else statement in there and assign some values to those variables in case the if statements don't evaluate to true. Post back here if that doesn't make the error go away.
Your other option is to initialize the variables to some default value when you declare them at the beginning of your code.
Give them a default value:
double lateFee=0.0;
double monthlyCharge = 0.0;
double annualRate = 0.0;
Basically, all possible paths don't initialize these variables.
Use the keyword "default"!!!
string myString = default;
double myDouble = default;
if(!String.IsNullOrEmpty(myString))
myDouble = 1.5;
return myDouble;
There are many paths through your code whereby your variables are not initialized, which is why the compiler complains.
Specifically, you are not validating the user input for creditPlan - if the user enters a value of anything else than "0","1","2" or "3", then none of the branches indicated will be executed (and creditPlan will not be defaulted to zero as per your user prompt).
As others have mentioned, the compiler error can be avoided by either a default initialization of all derived variables before the branches are checked, OR ensuring that at least one of the branches is executed (viz, mutual exclusivity of the branches, with a fall through else statement).
I would however like to point out other potential improvements:
Validate user input before you trust it for use in your code.
Model the parameters as a whole - there are several properties and calculations applicable to each plan.
Use more appropriate types for data. e.g. CreditPlan appears to have a finite domain and is better suited to an enumeration or Dictionary than a string. Financial data and percentages should always be modelled as decimal, not double to avoid rounding issues, and 'status' appears to be a boolean.
DRY up repetitive code. The calculation, monthlyCharge = balance * annualRate * (1/12)) is common to more than one branch. For maintenance reasons, do not duplicate this code.
Possibly more advanced, but note that Functions are now first class citizens of C#, so you can assign a function or lambda as a property, field or parameter!.
e.g. here is an alternative representation of your model:
// Keep all Credit Plan parameters together in a model
public class CreditPlan
{
public Func<decimal, decimal, decimal> MonthlyCharge { get; set; }
public decimal AnnualRate { get; set; }
public Func<bool, Decimal> LateFee { get; set; }
}
// DRY up repeated calculations
static private decimal StandardMonthlyCharge(decimal balance, decimal annualRate)
{
return balance * annualRate / 12;
}
public static Dictionary<int, CreditPlan> CreditPlans = new Dictionary<int, CreditPlan>
{
{ 0, new CreditPlan
{
AnnualRate = .35M,
LateFee = _ => 0.0M,
MonthlyCharge = StandardMonthlyCharge
}
},
{ 1, new CreditPlan
{
AnnualRate = .30M,
LateFee = late => late ? 0 : 25.0M,
MonthlyCharge = StandardMonthlyCharge
}
},
{ 2, new CreditPlan
{
AnnualRate = .20M,
LateFee = late => late ? 0 : 35.0M,
MonthlyCharge = (balance, annualRate) => balance > 100
? balance * annualRate / 12
: 0
}
},
{ 3, new CreditPlan
{
AnnualRate = .15M,
LateFee = _ => 0.0M,
MonthlyCharge = (balance, annualRate) => balance > 500
? (balance - 500) * annualRate / 12
: 0
}
}
};
Your assignments are all nested within your conditional if blocks which means that there is potential for them to never be assigned.
At the top of your class, initialise them to 0 or some other value
The compiler is saying that annualRate will not have a value if the CreditPlan is not recognised.
When creating the local variables ( annualRate, monthlyCharge, and lateFee) assign a default value (0) to them.
Also, you should display an error if the credit plan is unknown.
Not all code paths set a value for lateFee. You may want to set a default value for it at the top.
You don't assign values outside of the if statements ... and it is possible that credit might be something other than 0, 1, 2, or 3, as #iomaxx noted.
Try changing the separate if statements to a single if/else if/else if/else. Or assign default values up at the top.
If you declare the variable "annualRate" like
class Program
{
**static double annualRate;**
public static void Main() {
Try it..
I'm quite new to programming so forgive me if this is a simple fix. I am consistently getting an error "Use of unassigned local variable" in regards to to my string variable "city". What's confusing me is that I also have a string variable called "centreName" which does not bring up the same error even though it is also not initialized.
The point of this program is to have users input data for the name of a Science Centre and the city it's located in. I have tried initializing the "city" variable to both string city = null and string city = "", but when I run the program, the output shows up blank as opposed to what the user entered. This is not the case with my "centreName" var.
I will include the coding for both classes I am using. Please disregard my methods as I have yet to tweak them to fit this program. They are older methods I used for something previous and the coding does not apply here.
This is the class in which I'm getting the error:
class ScienceCentreApp
{
static void Main(string[] args)
{
string centreName;
string city;
decimal ticketPrice = 0;
int visitorCnt;
string[] dArray = new String[5];
int[] adultVisitors = new int[5];
decimal[] totalRevenue = new decimal[5];
char enterMoreData = 'Y';
ScienceCentreVisitation scv;
do
{
visitorCnt = GetData(out centreName, city, ticketPrice, dArray, adultVisitors, totalRevenue);
scv = new ScienceCentreVisitation(centreName, city, ticketPrice, dArray, adultVisitors, totalRevenue);
Console.Clear();
Console.WriteLine(scv);
Console.Write("\n\n\n\nDo you want to enter more data - " +
"(Enter y or n)? ");
if (char.TryParse(Console.ReadLine(), out enterMoreData) == false)
Console.WriteLine("Invalid data entered - " +
"No recorded for your respones");
} while (enterMoreData == 'y' || enterMoreData == 'y');
Console.ReadKey();
}
public static int GetData(out string centreName, string city, decimal ticketPrice, string[] dArray, int[] adultVisitors, decimal[] totalRevenue)
{
int i,
loopCnt;
Console.Clear();
Console.Write("Name of Centre: ");
centreName = Console.ReadLine();
Console.Write("City: ");
city = Console.ReadLine();
Console.Write("Ticket Price: ");
string inValue = Console.ReadLine();
ticketPrice = Convert.ToDecimal(inValue);
Console.Write("How many records for {0}? ", centreName);
string inValue1 = Console.ReadLine();
if (int.TryParse(inValue1, out loopCnt) == false)
Console.WriteLine("Invalid data entered - " +
"0 recorded for number of records");
for (i = 0; i < loopCnt; i++)
{
Console.Write("\nDate (mm/dd/yyyy): ");
dArray[i] = Console.ReadLine();
if (dArray[i] == "")
{
Console.WriteLine("No date entered - " +
"Unknown recorded for visits");
dArray[i] = "Unknown";
}
Console.Write("Number of One-Day Adult Visitors: ");
inValue1 = Console.ReadLine();
if (int.TryParse(inValue1, out adultVisitors[i]) == false)
Console.WriteLine("Invalid data entered - " +
"0 recorded for adults visited");
}
return i;
}
}
}
This is the other class that the first is calling:
class ScienceCentreVisitation
{
private string centreName;
private string city;
private decimal ticketPrice;
private string[] visitDate;
private int[] adultVisitors;
private decimal[] totalRevenue;
//constructors
public ScienceCentreVisitation()
{
}
public ScienceCentreVisitation(string cent)
{
centreName = cent;
}
public ScienceCentreVisitation(string cent, string cit, decimal price, string[] date, int[] visit, decimal[] rev)
{
visitDate = new string[date.Length];
adultVisitors = new int[visit.Length];
totalRevenue = new decimal[rev.Length];
Array.Copy(date, 0, visitDate, 0, date.Length);
Array.Copy(visit, 0, adultVisitors, 0, adultVisitors.Length);
Array.Copy(rev, 0, totalRevenue, 0, rev.Length);
centreName = cent;
city = cit;
ticketPrice = price;
}
//properties
public string CentreName
{
get
{
return centreName;
}
set
{
centreName = value;
}
}
public string City
{
get
{
return city;
}
set
{
city = value;
}
}
public decimal TicketPrice
{
get
{
return ticketPrice;
}
set
{
ticketPrice = value;
}
}
public string[] VisitDate
{
get
{
return visitDate;
}
set
{
visitDate = value;
}
}
public int[] AdultVisitors
{
get
{
return adultVisitors;
}
set
{
adultVisitors = value;
}
}
public decimal[] TotalRevenue
{
get
{
return totalRevenue;
}
set
{
totalRevenue = value;
}
}
//methods
public decimal CalculateTotalRevenue()
{
decimal totalRev;
int cntOfValidEntries;
int total = 0;
foreach (int c in adultVisitors)
total += c;
cntOfValidEntries = TestForZeros();
totalRev = (decimal)total / cntOfValidEntries;
return totalRev;
}
public int TestForZeros()
{
int numberOfTrueVisits = 0;
foreach (int cnt in adultVisitors)
if (cnt != 0)
numberOfTrueVisits++;
return numberOfTrueVisits;
}
public int GetIndexOfLeastVisited()
{
int minVisIndex = 0;
for (int i = 1; i < adultVisitors.Length; i++)
if (adultVisitors[i] > adultVisitors[minVisIndex])
minVisIndex = i;
return minVisIndex;
}
public int GetLeastVisited()
{
return adultVisitors[GetIndexOfLeastVisited()];
}
public string GetDateWithLeastVisited()
{
return visitDate[GetIndexOfLeastVisited()];
}
public int GetIndexOfMostRevenue()
{
int maxRevIndex = 0;
for (int i = 1; i < totalRevenue.Length; i++)
if (totalRevenue[i] > totalRevenue[maxRevIndex])
maxRevIndex = i;
return maxRevIndex;
}
public decimal GetMostRevenue()
{
return totalRevenue[GetIndexOfMostRevenue()];
}
public string GetDateWithMostRevenue()
{
return visitDate[GetIndexOfMostRevenue()];
}
public override string ToString()
{
return "Name of Centre: " + centreName +
"\nCity: " + city +
"\nDate of Least One-Day Adult Visitors:\t\t" + GetDateWithLeastVisited() +
"\nNumber of Least One-Day Adult Visitors: \t\t" + GetLeastVisited() +
"\nDate of Most Total Revenue Collected:\t\t" + GetDateWithMostRevenue() +
"\nHighest Total Revenue Collected:\t\t" + GetMostRevenue();
}
}
}
Thanks in advance!
The difference between centreName and city is that centreName is used as an out parameter. That means you can call the method with an uninitialized variable because it is guaranteed that the variable will be assigned a value inside the method.
In your case, both centreName and city are assigned values in GetData, so you can safely replace string city with out string city.
centreName is declared as an out parameter (meaning it will retain the last value you assign to it inside the method when you leave the method's scope), so it is getting initialized by get data. The rest of your variables are not being set by the get data call, because they are not out parameters.
On that point, out parameters are generally considered bad practice in C# (I can't speak to other languages). Most they obscure the purpose of the method, and it is very easy to accidentally overwrite the value of one of the out parameters after the method is called (or mistakenly initialize it). Instead of using out parameters, create an object that wraps your data and return that instead of an int.
The program works in its current for but I would like to make it more object oriented like its design using constructors and such but I don't know where to start and how to make it work. I would like your insight and examples on how you would accomplish this task? Here is a sample UML for what I am trying to do.
Original Design
public static class IsbnConsole
{
public static void Main(string[] args)
{
Console.Write("Enter a valid 10 digit ISBN Number ");
string isbn = checkIsbnClass.DestabilizeIsbn(Console.ReadLine()); // Normalizes the input and puts it on string "str"
if (isbn.Length > 10 || isbn.Length < 9) // If the string length is greather than 10, or smaller than 9
{
Console.WriteLine("The number you have entered is not a valid ISBN try again."); // Print invalid number
Console.ReadLine();
}
else if (isbn.Length == 10) // If the length is 10
{
if (checkIsbnClass.CheckNumber(isbn)) // If function CheckNum return "true"...
Console.WriteLine("The number you have entered is a Valid ISBN");
else // If it returns "false"...
Console.WriteLine("The number you have entered is not a valid ISBN try again.");
Console.ReadLine();
}
else // Else (If the number is NOT greater than 10 or smaller than 9, NOR is it 10 -> If the number is 9)
{
Console.WriteLine("The Check digit that corresponds to this ISBN number is " + checkIsbnClass.CheckIsbn(isbn) + "."); // Print the checksum digit
Console.ReadLine();
}
public static class checkIsbnClass
{
public static string CheckIsbn(string isbn) // Calculates the 10th digit of a 9-digits partial ISBN number
{
int sum = 0;
for (int i = 0; i < 9; i++) // For each number...
{
sum += int.Parse(isbn[i].ToString()) * (i + 1); // ...Multiply the number by it's location in the string
}
if ((sum % 11) == 10) // If the remainder equals to 10...
{
return "x"; // Output X
}
else // If it does not equal to 10...
{
return (sum % 11).ToString(); // Output the number
}
}
public static bool CheckNumber(string isbn) // Checks if the checksum digit is correct
{
if (isbn[9].ToString() == CheckIsbn(isbn)) // If the 10th digit of the number is the same as the calculated digit...
return true;
else // If they're not the same...
return false;
}
public static string DestabilizeIsbn(string isbn) // replace the string
{
return isbn.Replace("-", "").Replace(" ", "");
}
Newly Designed Method Using Constructors
public class isbn
{ //attributes
private string isbnNum;
//method
public string GetIsbn()
{
return this.isbnNum;
}
//constructor
public isbn()
{
Console.Write("Enter Your ISBN Number: ");
this.isbnNum = Console.ReadLine();
}//end default constructor
//method
public string displayISBN()
{
return this.GetIsbn();
}
public static void Main(string[] args)
{
//create a new instance of the ISBN/book class
isbn myFavoriteBook = new isbn();
//contains the method for checking validity
bool isValid = CheckDigit.CheckIsbn(myFavoriteBook.GetIsbn());
//print out the results of the validity.
Console.WriteLine(string.Format("Your book {0} a valid ISBN",
isValid ? "has" : "doesn't have"));
Console.ReadLine();
}
public static class CheckDigit
{ // attributes
public static string NormalizeIsbn(string isbn)
{
return isbn.Replace("-", "").Replace(" ", "");
}
public static bool CheckIsbn(string isbn) // formula to check ISBN's validity
{
if (isbn == null)
return false;
isbn = NormalizeIsbn (isbn);
if (isbn.Length != 10)
return false;
int result;
for (int i = 0; i < 9; i++)
if (!int.TryParse(isbn[i].ToString(), out result))
return false;
int sum = 0;
for (int i = 0; i < 9; i++)
sum += (i + 1) * int.Parse(isbn[i].ToString());
int remainder = sum % 11;
if (remainder == 10)
return isbn[9] == 'X';
else
return isbn[9] == (char)('0' + remainder);
}
public static class IsbnConsole
{
public static void Main(string[] args)
{
Console.Write("Enter a valid 10 digit ISBN Number ");
string isbn = checkIsbnClass.DestabilizeIsbn(Console.ReadLine()); // Normalizes the input and puts it on string "str"
Isbn isbn = new Isbn(Console.In)
if (!isbn.CheckLength())
Console.WriteLine("The number you have entered is not a valid ISBN try again."); // Print invalid number
}
else if (isbn.HasCheckDigit)
{
if (isbn.CheckNumber(isbn))
Console.WriteLine("The number you have entered is a Valid ISBN");
else
Console.WriteLine("The number you have entered is not a valid ISBN try again."); // Print invalid number
}
else
{
Console.WriteLine("The Check digit that corresponds to this ISBN number is " + isbn.GetCheckDigit(isbn) + "."); // Print the checksum digit
}
Console.ReadLine();
}
public class Isbn
{
public Isbn(TextReader cin)
{
/// do stuff here.
}
public bool CheckLength()
{
/// do stuff here.
}
public bool HasCheckDigit { get { ..... } }
public int GetCheckDigit() {..... }
public bool CheckNumber() {......}
}
I do think the original design makes sense. I mean the ISBN has no other purpose than to be checked. I don't see any benefit in trying to materialize it as an instance class.
But just for academic purposes, you may ask yourself several questions here:
- Do you want to materialize the ISBN itself ? Create an ISBN class containing properties for its value and methods for the checks (this is James Curran's solution)
- Do you want to materialize the fact that you check an ISBN ? Create an ISBNChecker class that will contain methods to perform the checks
- Both ? an ISBN instance will be created from the Console input and pass as a parameter to the ISBNChecker instance.
By materialize, I mean "create an instance class"