Adding cost to a ListBox - c#

I currently have a listbox that displays the date, type of cake and size. I want to add cost to the listbox, but I am having trouble. It currently displays zero for the cost. The cost is displayed in a label (lblRoundCost). I have a base class named Cake and two subclasses RoundCake and SquareCake. I'm not sure if this code is correct for the base class:
class Cake
{
private const int CostOfFoodPerPerson = 25;
public int size;
private bool chocolateIcing;
protected DateTime cakeDate;
decimal cost;
public Cake(int numberOfPeople, bool chocolateIcing, DateTime cakeDate)
{
this.chocolateIcing = chocolateIcing;
Size = size;
this.cakeDate = cakeDate;
Cost = cost;
}
public virtual decimal Cost
{
get { return cost; }
set { cost = value; }
}
public virtual int Size
{
get { return size; }
set { size = value; }
}
public virtual bool ChocolateIcing
{
set { chocolateIcing = value; }
}
public virtual decimal CalculateCost()
{
decimal CostOfIcing = 0;
if (chocolateIcing)
CostOfIcing = (Size * 1.5M) + 10M;
else
CostOfIcing = 0;
decimal TotalCost = CostOfIcing + CostOfFoodPerPerson;
return TotalCost;
}
public DateTime CakeDate
{
set { cakeDate = value; }
}
}
}
RoundCake code
class RoundCake : Cake
{
bool fruitOption;
public RoundCake(int size, bool fruitOption, bool chocolateIcing, DateTime cakeDate)
: base(size, chocolateIcing, cakeDate)
{FruitOption = fruitOption;}
public bool FruitOption
{
set { fruitOption = value; }
}
public override decimal CalculateCost()
{
decimal totalCost;
if (fruitOption)
{
totalCost = base.CalculateCost();
return totalCost + (totalCost * .05M);
}
else
{
totalCost = base.CalculateCost() ;
return totalCost;
}
}
public override string ToString()
{
return String.Format("{0,-20}{1,2}{2,20}{2,20}", cakeDate.ToShortDateString(), "RC",Size,Cost);
}
Form1 code
private void btnRound_Click_1(object sender, EventArgs e)
{
lstCake.Items.Add(roundCake);
}
roundCake = new RoundCake((int)nudRound.Value, chbFruit.Checked, chbChocoRound.Checked,
dtpRound.Value.Date);
lblRoundCost.Text = roundCake.CalculateCost().ToString("c");

The reason you're seeing 0 is because you never actually assign anything to Cost, and the default for decimal is 0.
Here's what's happening:
In your base constructor, you have:
Cost = cost;
However, cost is never initialized in the class and it's not passed in via the constructor. So in the base it's 0.
The same thing happens with the inheriting class - Cost is never specified, so it's still going to be 0 (even if there wasn't a base class, it'd still be 0).
Now, in this line of code:
lblRoundCost.Text = roundCake.CalculateCost().ToString("c");
You're assigning the value calculated by CalculateCost() to the Label, but you're never persisting that value in the the class:
public override decimal CalculateCost()
{
decimal totalCost;
if (fruitOption)
{
totalCost = base.CalculateCost();
return totalCost + (totalCost * .05M);
}
else
{
totalCost = base.CalculateCost() ;
return totalCost;
}
}
You're returning a value, but not assigning it to the class member cost. The base implementation does the same thing.
There a number of ways to solve this. Here's one (this is a pretty simple one, and to be honest it has a bit of a code smell to me, but it'll server as an example):
Modify the CalculateCost() method to update the cost field:
public virtual void CalculateCost()
{
decimal CostOfIcing = 0;
if (chocolateIcing)
CostOfIcing = (Size * 1.5M) + 10M;
else
CostOfIcing = 0;
decimal cost = CostOfIcing + CostOfFoodPerPerson;
}
Note that this doesn't return a type anymore (you may have it still do so, it really depends a lot on your overall design, so pick the path that works best for your design). Don't forget to make this change in the inheriting class's implementation as well.
Now you simply need to call the CalculateCost() method and you will have the cost available, and you can use the property to get the cost for assignment to Labels or whatever else you need, and it will show up in your overridden ToString() method.
Again, there are multiple ways to solve this, and they depend on a mix of OOP principles and your design needs. My main intention with this answer was to demonstrate why cost was showing up as zero.

Related

Can you do calculations in the get/set ? c#

Can you do a calculation in the set clause? and it then returns the total when implemented?`
public decimal TotalCost
{
set
{ this.costTotal = (decimal)prodCost + (decimal)shipping + (decimal)insurance)}
get
{ return this.costTotal}
}
Can you do a calculation in the set clause?
Absolutely. However, in your specific case, it is not clear why would you do that. The point of a setter is to allow users of a class to safely manipulate fields of its objects. This is done using the value keyword. Since you are only interested in calculating a value using existing data, there is no reason to even use a setter. it seems more suitable to do the calculation in a getter only property:
public decimal TotalCost
{
get
{
return (decimal)prodCost + (decimal)shipping + (decimal)insurance);
}
}
A shorter version of the above code:
public decimal TotalCost => (decimal)prodCost + (decimal)shipping + (decimal)insurance;
What others said, but maybe you're looking for a method:
public decimal CostTotal { get; private set; }
(...)
public void SetTotalCost(decimal prodCost, decimal shipping, decimal insurance)
{
this.CostTotal = prodCost + shipping + insurance);
}
I suggest the below code for reading and writing your property.
private decimal totalCost;
public decimal TotalCost
{
get { return totalCost = (decimal)prodCost + (decimal)shipping + (decimal)insurance);}
set { totalCost = value;}
}

unable to change the value of a property

I am currently improving my program that I posted on CR but I ran into a problem. I have a property called Total but when I try to set it to a value (0) it remains the same.
This is the property:
public class Player
{
private int total;
public int Total
{
get
{
total = 0;
foreach (int card in hand)
{
total += card;
}
return total;
}
set { this.total = value; }
}
}
And here is how I try to change it:
public class Game
{
private void CompareHands()
{
//This is just for testing
Console.WriteLine($"player total: {player1.Total}, is bust: {player1.Bust}");
Console.WriteLine($"house total: {house.Total}, is bust: {house.Bust}");
if (player1.Bust)
player1.Total = 0;
if (house.Bust)
house.Total = 0;
//this too
Console.WriteLine($"player total: {player1.Total}, is bust: {player1.Bust}");
Console.WriteLine($"house total: {house.Total}, is bust: {house.Bust}");
...
}
Also the Bust property if needed:
private readonly int blackjack = 21;
public bool Bust
{
get { return Bust = Total > blackjack; }
private set { }
}
Actually you're recalculating the total everytime you call the getter of your property.
A solution is to make the field total as Nullable<int> so if it is null, you do the logic you're doing actually otherwise return what is set in the field total.
public class Player
{
private int? total; // <- Nullable<int> here
public int Total
{
get
{
if(total.HasValue) // <- If value is set return that value.
{
return total.Value;
}
total = 0;
foreach (int card in hand)
{
total += card;
}
return total.Value;
}
set { this.total = value; }
}
}
If I were you I would separate the Total calculation and Bust a bit differently:
public class Player
{
public bool Bust { get; set; }
public int GetTotal()
{
if (Bust)
{
return 0;
}
var total = 0;
foreach (int card in hand)
{
total += card;
}
return total;
}
}
A few things to notice:
the calculation is done in a method not a property - i think this is a cleaner way since a property is supposed to be quite simple and not have any logic in it
include Bust in the GetTotal calculation and return 0 if Bust is set to true
always compute the total value, unless you have a very good reason to have a cached version of it
Hope this helps.

Should data be saved to a file if that data can simply be calculated later?

I am creating a program that records data for a sprinter who runs the 100 meters. To do this, I designed a class named the "Entry" class that is used to record the date the user ran the 100m and the seconds that the user ran it in. When an Entry object is created, the user enters the date and seconds, and the objects calculates some other data such as the average speed of the run and the (estimated) top speed. When the user saves the "Entry", the data is written to a file (which contains other entries from previous dates in it). My question is should just the date and seconds be written to the file and the other information (such as average/top speed) be calculated again when the data is loaded from the file? Or should I just write all the data to the file so that the information doesn't need to be calculated again?
Note: A concern I have about writing all the data to the file is that I would have to create some sort of method, likely a constructor, for setting the average/top speed in the entry object (when the data is loaded from the file). This means that someone using this object could set the average/top speed themselves, which I don't want. I want the object itself to be in charge of determining that info, nobody else.
I included some of the code for the class just in case it helps.
Any help or suggestions is greatly appreciated!
class Entry
{
// Class fields
private const int DISTANCE = 100;
private const double ADDEDMPH = 3.5;
private int _month;
private int _day;
private int _year;
private double _seconds;
private double _averageSpeed = 0;
private double _topSpeed;
// Constructors
public Entry()
{ }
public Entry(double secs)
{
_seconds = secs;
validateSeconds();
if(secondsChecked)
calcTopAndAverageSpeed();
}
public Entry(int month, int day, int year)
{
_month = month;
_day = day;
_year = year;
validateDate();
}
public Entry(double secs, int month, int day, int year)
{
_seconds = secs;
_month = month;
_day = day;
_year = year;
validateAll();
if(secondsChecked)
calcTopAndAverageSpeed();
}
// Class properties - when something is entered, it should be checked. If something is invalid, when it is accessed, it should return -1
public double Seconds
{
get
{
if (secondsChecked == true)
return _seconds;
else
return -1;
}
set
{
_seconds = value;
validateSeconds();
if(secondsChecked)
calcTopAndAverageSpeed();
}
}
public int Day
{
get
{
if (dayChecked == true)
return _day;
else
return -1;
}
set
{
_day = value;
validateDay();
}
}
public int Month
{
get
{
if (monthChecked == true)
return _month;
else
return -1;
}
set
{
_month = value;
validateMonth();
}
}
public int Year
{
get
{
if (yearChecked == true)
return _year;
else
return -1;
}
set
{
_year = value;
validateYear();
}
}
// Class Methods
// Calculating methods
void calcAverageSpeed()
{
_averageSpeed = (DISTANCE / _seconds);
}
void calcTopSpeed()
{
_topSpeed = ((DISTANCE / _seconds) + ADDEDMPH);
}
void calcTopAndAverageSpeed()
{
_averageSpeed = (DISTANCE / _seconds);
_topSpeed = _averageSpeed + ADDEDMPH;
}
}
If you are saving all that information in the database, you will need space for it. Also during insertion, you will need to calculate it so insertion will take longer (not significantly longer, but this depends on your performance goals). If you do not save it, then you will need less space but it will need to be calculated during retrieval.
Therefore, it all depends on your requirements. If you do not have any specific requirements, then I would not save it in the database because they can be computed and there is no restriction (requirement) that it should not be computed during retrieval.
Again, depends on the situation.

StackOverflow Exception from get and set

I have the following code:
namespace QuantStrats
{
class Program
{
static void Main(string[] args)
{
string FilePath = "C:\\Users\\files\\DJ.csv";
StreamReader streamReader = new StreamReader(FilePath);
string line;
List<Data> Data = new List<Data>();
while ((line = streamReader.ReadLine()) != null)
{
Data Tick = new Data();
string [] values = line.Split(',');
Tick.SetFields(values[1], values[2]);
Data.Add(Tick);
}
for (int ii = 0; ii < Data.Count; ii++)
{
Data TickDataValues = new Data();
TickDataValues = Data[ii];
Console.Write("TIME :" + TickDataValues.time + " Price : " + TickDataValues.price + Environment.NewLine);
}
Console.ReadLine();
}
}
class Data
{
public DateTime time
{
get { return this.time; }
set
{
this.time = value;
}
}
public double price
{
get { return this.price; }
set
{
this.price = value;
}
}
public void SetFields(string dateTimeValue, string PriceValue)
{
try
{
this.time = Convert.ToDateTime(dateTimeValue);
}
catch
{
Console.WriteLine("DateTimeFailed " + dateTimeValue + Environment.NewLine);
}
try
{
this.price = Convert.ToDouble(PriceValue);
}
catch
{
Console.WriteLine("PriceFailed " + PriceValue + Environment.NewLine);
}
}
}
}
But I get a stack overflow exception.
I know it is because I am not doing my get and sets correctly and am entering an infinite loop, but I cannot see why exactly this is happening?
public DateTime time
{
get { return this.time; }
set
{
this.time = value;
}
}
you aren't using backing fields, but setting the property itself from within the property setter.
You can fix this by using 1) an auto property
public DateTime Time { get; set; }
or 2) a backing field
private DateTime _time;
public Datetime Time
{
get { return _time; }
set { _time = value; }
}
they both equate to the same code.
For an explanation, when you get time in your code:
get { return this.time; }
it has to retrieve the value of time to return. It does that by calling the get on time, which has to get retrieve the value of time, etc.
I cannot see why exactly this is happening?
public double price
{
get { return this.price; }
set
{
this.price = value;
}
}
When you "get" price, the getter for price is called, which calls the getter for price, which calls the getter for price, which...
Just use auto-implement properties if you don't want to mess with a backing field:
public DateTime Time {get; set;}
public double Price {get; set;}
Some other observations:
The standard convention for property names is to start them with a capital letter, which is why I changed your properties to Time and Price in my examples.
You may want to consider using decimal for a property like Price if you do any floating-point math, since double has some slight imprecision when representing decimal numbers like 1.1. decimal will store the number exacly without any loss of precision.
Just writing to the console in a catch block seems incorrect. You are basically ignoring the error (from a logic flow sense). Rather than accepting strings in the class and parsing them, I would do the validation in the calling code and making sure the inputs are valid before passing them to the class.
Properties getters and setters are really just getXXX and setXXX methods (that's how they are compiled). Because you set the property from the property itself, it is if you were recurring endlessly on a method.
public DateTime time()
{
return time();
}
As stated by other answers, you can use backing fields or auto-implemented properties.

Use property value in another property setter

In my Class i need to set one property value according to another:
public class Quantities
{
private int _quant;
public int Quant
{
get { return _quant; }
set
{
if (Unit == "K")
{
_quant = value / 1000;
}
else
{
_quant = value;
}
}
}
public string Unit { get; set; }
}
according to several tests i made it works fine but i still don't know if it's safe to do this.
is it possible that the Quant Property will be evaluated before the Unit Property or does the compiler (or JIT) knows that it should assign the Unit Property first?
This has nothing to do with the compiler or the JIT. Your code assigns the values. You need to know the order in which they should be assigned.
BTW: Your code exhibits temporal coupling. It would be better to make at least the Unit unchangeable by making the property readonly and by providing a constructor that requires the unit:
public class Quantities
{
private readonly string _unit;
private int _quant;
public Quantities(string unit)
{
if(unit == null) throw new ArgumentNullException("unit");
_unit = unit;
}
public int Quant
{
get { return _quant; }
set
{
if (Unit == "K")
{
_quant = value / 1000;
}
else
{
_quant = value;
}
}
}
public string Unit { get { return _unit; } }
}
This class now can't be used in an incorrect way.
For more points that can be improved with your class, please refer to Lasse's answer.
Code on the outside of this class must know about this dependency or you risk someone changing Unit without re-setting Quant:
var x = new Quantities(); // why no constructor for this?
x.Unit = "K";
x.Quant = 1700; // why int? this will now be 1, not 1.7
x.Unit = "M";
Personally I would make the class a struct, and make it immutable:
public struct Quantity
{
private readonly double _Value;
private readonly string _Unit;
public Quantity(double value, string unit)
{
_Value = value;
_Unit = unit;
}
public double Value
{
{
return _Value;
}
}
public double Unit
{
{
return _Unit;
}
}
}
Also note that I did not change the value at all, hence:
var x = new Quantity(1700, "K");
means 1700K, not 1.7K. I would refrain from doing such "automagical" interpretations of data. If you need to display the value with a different unit, I would instead build in a conversion system:
public Quantity ConvertToUnit(string newUnit)
{
var newValue = ... calculate value with new unit
return new Quantity(newValue, newUnit);
}
The class is not a good design. Do not do this.
Consider the following code:
Quantities q1 = new Quantities { Unit = "K", Quant = 1000};
Console.WriteLine(q1.Quant); // Prints 1
// Make a copy of q1
Quantities q2 = new Quantities{ Unit = q1.Unit, Quant = q1.Quant };
Console.WriteLine(q2.Quant); // Prints 0
You would expect that making a copy of the Quantities would work by doing a basic copy like the above. That it does not shows you how dangerous this kind of design is.
This is still a problem after making the changes in the accepted answer above
If you use the changes that Daniel suggested, you still have the nastyness associated with your property setter and getter not being commutative. Sure, you would be forced to pass the units into the constructor, but the object copy still won't work as the user might expect:
Quantities q1 = new Quantities("K"){ Quant = 1000};
Console.WriteLine(q1.Quant); // Prints 1
// Make a copy of q1
Quantities q2 = new Quantities(q1.Unit){ Quant = q1.Quant };
Console.WriteLine(q2.Quant); // STILL Prints 0

Categories