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;}
}
Related
I don't know if I have chosen a correct headline, but I do hope I have.
I am currently trying to get a better understand of methods in C#, and in doing so I thought that I'd make a simple BankAccount example.
So, what I have is this:
I have three methods:
a method to "deposit" money.
a method to "withdraw" money.
a method to print everything (name, balance).
class BankAccount
{
public string Name
{
get { return _Name; }
set { _Name = value; }
}
private string _Name;
public int Balance
{
get { return _Balance; }
set { _Balance = value; }
}
private int _Balance;
public BankAccount(string name)
{
Name = name;
Balance = 1000;
}
// deposit money
public int Deposit(int balance)
{
Balance += balance;
return Balance;
}
// withdraw money
public int WithDraw(int balance)
{
Balance -= balance;
return Balance;
}
// print to console
public void Print()
{
Console.WriteLine("Owner: " + Name
+ "\nYour current balance is: $" + Balance);
}
}
What I want to do is this:
if I call "deposit" in Main and pass it a value, I want the print method to show me the amount (same goes for "withdraw").
How do I achieve this? I have tried some controle statements, but I don't know how to do this correctly with methods that have parameters?
I hope that someone can shed some light on this issue of mine.
What you can do is overload the method to do more than one thing, for example you can create an overload that takes an int (the balance being subtracted or added) and a string saying which action happening, then you can have this method in the code along with the already existing one
public void Print(int balance, string action)
{
Console.WriteLine("Owner: " + Name
+ "\nYour current balance is: $" + Balance
+ "and you " + action + ": $" + balance);
}
This could be used by passing the string action as "withdrew" or "deposited" depending on which method calls it.
Using this overload allows you to both output the original Print method, if they want to know their balance but never withdrew or deposited, and the new version all depending on which parameters you pass
For more information on overloading see this MSDN page
Example Usage:
public int Deposit(int balance)
{
Balance += balance;
Print(balance, "deposited"); //prints current balance AND action that was completed
return Balance;
}
public void ShowBalance()
{
Print(); //Just prints current balance
}
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.
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.
Why am I getting a "the name does not exist in the current context" error on this C# program?
Here is my class file:
namespace Exercise8
{
class Park
{
Park aPark = new Park();
private string name;
public string Name
{
get
{
name = Console.ReadLine();
return name;
}
set
{
}
}
private string location;
public string Location
{
get
{
location = Console.ReadLine();
return location;
}
set
{
}
}
private string facilityType;
public string FacilityType
{
get
{
facilityType = Console.ReadLine();
return facilityType;
}
set
{
}
}
private string facilitiesAvailable;
public string FacilitiesAvailable
{
get
{
facilitiesAvailable = Console.ReadLine();
return facilitiesAvailable;
}
set
{
}
}
private double fee;
public string sFee;
public double Fee
{
get
{
fee = double.Parse(sFee);
sFee = Console.ReadLine();
return fee;
}
set
{
}
}
private int noOfEmployees;
public string sNoOfEmployees;
public int NoOfEmployees
{
get
{
noOfEmployees = int.Parse(sNoOfEmployees);
sNoOfEmployees = Console.ReadLine();
return noOfEmployees;
}
set
{
}
}
//variables for Cost Per Visitor.
//noVisitors will be used in Calculate Revenue
public double costPerVisitor;
public double annualBudget = 200000;
public double noVisitors = 10000;
public double CalculateCostPerVisitor(double annualBudget, double noOfVisitors)
{
//divide annual budget by number of visitors
return costPerVisitor = annualBudget / noVisitors;
}
//Calculate Revenue
public double revenue;
public double CalculateRevenue(double noOfVisitors,double fee)
{
//No of Visitors times Fee
revenue = noVisitors * fee;
return revenue;
}
public override string ToString()
{
return "Name of park: " + this.Name + "\nLocation: " + this.Location + "\nFacility Type: " + this.FacilityType + "\nFacility Fee: " + this.Fee + "\nNumber of employees: " + this.NoOfEmployees +
"\nNumber of visitors recorded in the past 12 months: " + this.noVisitors + "Annual Budget: " + this.annualBudget;
}
}
}
Here is my program:
namespace Exercise8
{
class Program
{
public static void main (String [] args)
{
//Instantiate Objects
Park aPark = new Park();
//Call Methods
Console.WriteLine("Name of park: " + aPark.Name + "; Location of park: " + aPark.Location + "; Type of park: " + aPark.FacilityType);
Console.WriteLine("Name of park: " + aPark.Name + "; Location of park: " + aPark.Location + "; Facilities available: " + aPark.FacilitiesAvailable);
Console.WriteLine("Annual cost per visitor: " + CalculateCostPerVisitor());
Console.WriteLine("Revenue: " + CalculateRevenue());
//Below should hopefully return To String
Console.WriteLine(aPark.ToString());
}
}
}
And these are the errors I'm seeing:
The name 'CalculateCostPerVisitor' does not exist in the current context
The name 'CalculateRevenue' does not exist in the current context line
Those methods are defined for objects of type Park, but you're trying to access them from inside Program without qualification or an object of type Park.
For static methods, you would need to qualify them with the class name. But these methods are not static.
For instance methods, you need to call them on a particular instance. You have an instance aPark, but you aren't using it when you tried to call its methods.
The methods are defined as instance methods on the Park class type, so you need to call the method with a reference to an instance of Park. Additionally, each method takes 2 parameters, so you'd have to provide them at the time you call the methods:
Console.WriteLine("Annual cost per visitor: " + aPark.CalculateCostPerVisitor( ... /* actual parameters here */));
Console.WriteLine("Revenue: " + aPark.CalculateRevenue( ... /* actual parameters here */));
However, since you've defined annualBudget, noOfVisitors, and fee as fields of your Park class, I think it's likely that you never really intended these values to passed in as parameters—or at the very least you're confused about whether these should really be parameters or if the class should calculate the result from the field values.
I'd recommend you remove these parameters, and simply calculate the results from the field values:
public double CalculateCostPerVisitor()
{
//divide annual budget by number of visitors
this.costPerVisitor = this.annualBudget / this.noVisitors;
return this.costPerVisitor;
}
public double CalculateRevenue()
{
//No of Visitors times Fee
this.revenue = this.noVisitors * this.fee;
return this.revenue;
}
...
Console.WriteLine("Annual cost per visitor: " + aPark.CalculateCostPerVisitor());
Console.WriteLine("Revenue: " + aPark.CalculateRevenue());
Not entirely relevant to the question, but there are a few other things wrong (or at least very strange) about your class:
Park aPark = new Park();
You are creating new instance of Park inside every Park, this is bound to lead to a stack overflow error if you try to create a single instance. You should remove this line from your class file.
name = Console.ReadLine();
You are reading from the console every time you try to get the value from a property. This is wrong on many levels. All of your properties should just get / set private values and try to do as little work as possible. If you want to allow the user to specify the name in the console, that should be done in your Main method like this:
aPark.Name = Console.ReadLine();
fee = double.Parse(sFee);
sFee = Console.ReadLine();
I'm not entirely sure what's going on here, but it's backwards. You need to read the input from the console first, then try to parse it. And again, it should be done in the Main method, look like this:
string sFee = Console.ReadLine();
aPark.Fee = double.Parse(sFee);
Once you've corrected the properties following the above steps, you can drop the private backing fields and dramatically simplify your code using automatic properties, like this:
public string Name { get; set; }
You should generally avoid public fields. If you keep costPerVisitor as a member of your class, you should probably make it a property, like this:
public double CostPerVisitor { get; set; }
Problem 1:
call the method with a reference to an instance of Park
aPark.CalculateCostPerVisitor(argument1, argument2);
Problem 2:
Also, CalculateCostPerVisitor() needs to have two arguments. There is not CalculateCostPerVisitor() method in your class.
You have
public double CalculateCostPerVisitor(double annualBudget, double noOfVisitors)
public double CalculateRevenue(double noOfVisitors,double fee)
I am upgrading an existing application that has implemented a home-brew Constants class in its business and datalayer objects.
I want to replace this with Nullable types and do-away with the constants class, that looks like this, but with all non-nullable data types:
class Constants
{
public static int nullInt
{
get { return int.MinValue; }
}
}
These constants vaules are used as defaults on almost all the object properties like this:
private decimal _unitPrice = Constants.nullInt;
public decimal UnitPrice
{
get { return _unitPrice; }
set { _unitPrice = (value == null) ? Constants.nullInt : value; }
}
This causes some confusion on saving object properties to the Db as all decimal's and ints have to be checked for psudo null values or else you save things like int.MinValue to the Db.
private void Save()
{
//Datalayer calls and other props omitted
SqlParameter sqlParm = new SqlParameter();
sqlParm.Value = (this.UnitPrice == Constants.nullInt) ? DBNull.Value : (object)this.UnitPrice;
}
Ok so now the question.. I want to change things around using Nullable value types as in my example below, will the change in a property from a decimal to a decimal? affect any code thats implementing these objects?
public decimal? UnitPrice { get; set; }
private void Save()
{
//Datalayer calls and other props omitted
SqlParameter sqlParm = new SqlParameter();
sqlParm.Value = this.UnitPrice ?? DBNull.Value;
}
EDIT: Thanks for the double check of my refactor, and yes the null check on the SET of the property in the original code would be redundant. I still want to know if code that implements this object could have any issues from the change of type to decimal? from decimal
public decimal? UnitPrice { get; set; }
private void Save()
{
//Datalayer calls and other props omitted
SqlParameter sqlParm = new SqlParameter();
sqlParm.Value = this.UnitPrice ?? DBNull.Value;
}
I find this absolutely ok. This is how it is supposed to work.
I'm a little unclear as to the implementation of your above property set method as a decimal can never be null so this test is redundant I think. I'm including some sample code that can be dropped into a Console application that should clear things up for you.
You will experience very little refactoring of your code due to switching over to the nullable data types. This will be a good move on your part in cleaning up your code and avoiding the potential pitfalls of your current implementation.
If nothing else, the performance gains you'll receive would likely make the effort worth your while. Nothing will be completely plug-n-play so to speak but if you look at the below code you'll see there's very little impact in the scenarios you've provided.
Example
using System;
using System.Data.SqlClient;
namespace NullableTypes
{
class Program
{
static class Constants
{
public static decimal NullDecimal
{
get { return decimal.MinValue; }
}
}
public class ProductTheOldWay
{
public string Name { get; set; }
public decimal UnitPrice { get; set; }
public ProductTheOldWay()
{
Name = string.Empty;
UnitPrice = Constants.NullDecimal;
}
public override string ToString()
{
return "Product: " + Name + " Price: " +
((UnitPrice == Constants.NullDecimal)
? "Out of stock"
: UnitPrice.ToString());
}
public void Save()
{
//Datalayer calls and other props omitted
var sqlParm = new SqlParameter
{
Value = (UnitPrice == Constants.NullDecimal)
? DBNull.Value
: (object)UnitPrice
};
//save to the database...
Console.WriteLine("Value written to the database: " + sqlParm.Value);
}
}
public class ProductTheNewWay
{
public string Name { get; set; }
public decimal? UnitPrice { get; set; }
public ProductTheNewWay()
{
Name = string.Empty;
}
public override string ToString()
{
return "Product: " + Name + " Price: " +
((UnitPrice.HasValue)
? UnitPrice.ToString()
: "Out of stock");
}
public void Save()
{
//Datalayer calls and other props omitted
var sqlParm = new SqlParameter
{
Value = UnitPrice
};
//save to the database...
Console.WriteLine("Value written to the database: " + sqlParm.Value);
}
}
static void Main()
{
var oldProduct1 = new ProductTheOldWay
{
Name = "Widget",
UnitPrice = 5.99M
};
var oldProduct2 = new ProductTheOldWay
{
Name = "Rare Widget",
UnitPrice = Constants.NullDecimal // out of stock
};
Console.WriteLine(oldProduct1);
Console.WriteLine(oldProduct2);
Console.WriteLine("Saving...");
oldProduct1.Save();
oldProduct2.Save();
Console.ReadLine();
var newProduct1 = new ProductTheNewWay
{
Name = "Widget",
UnitPrice = 5.99M
};
var newProduct2 = new ProductTheNewWay
{
Name = "Rare Widget"
/* UnitPrice = null by default */
};
Console.WriteLine(newProduct1);
Console.WriteLine(newProduct2);
Console.WriteLine("Saving...");
newProduct1.Save();
newProduct2.Save();
Console.ReadLine();
// as a further example of the new property usage..
if (newProduct1.UnitPrice > 5)
Console.WriteLine(newProduct1);
Console.WriteLine("Using nullable types is a great way to simplify code...");
Console.ReadLine();
}
}
}
Output
Product: Widget Price: 5.99
Product: Rare Widget Price: Out of stock
Saving...
Value written to the database: 5.99
Value written to the database:
Product: Widget Price: 5.99
Product: Rare Widget Price: Out of stock
Saving...
Value written to the database: 5.99
Value written to the database:
Product: Widget Price: 5.99
Using nullable data types is a great way to simplify code...
Let me know if there are more specific implementation details that concern you making the switch.