I am trying to write a program with the instructions below. The program does work currently, but it does not fully meet the specs.
I think I'm very close, but I'm having trouble understanding the constructor setup and how to do the current year.
Coding Instructions
Create a class called Car.
This class should have 3 member variables:
a string called Make
a string called Model
an integer called Year
The class should have a function that returns the age of the car by subtracting the member variable "year" from the current year (2021).
The class should have a constructor that takes 3 parameters: make, model, year.
This is my code:
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Welcome to Historic Car Calculator");
Console.WriteLine("Please enter the make of your car");
var _Make = Console.ReadLine();
Console.WriteLine("Please enter the model of your car.");
var _Model = Console.ReadLine();
Console.WriteLine("Please enter the year your car was made.");
int _Year = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Your " + _Make + " " + _Model+ " is " + (2021 - _Year) + " years old");
}
public class Car
{
public string Make;
public string Model;
public int Year;
public int currentYear;
//Overloaded Constructor
public Car(string _Make, string _Model, int _Year)
{
Make = _Make;
Model = _Model;
Year = _Year;
}
public void display()
{
var Car = new Car(Make, Model, Year);
}
public void CarAge(int Year)
{
this.currentYear = 2021 - this.Year;
}
}
}
1.You need to declare those 3 member variables as private, if they are public it can be accessed (for both getting and setting values), and that's what we don't want. In your case constructor should be the only means of setting their values:
private string Make;
private string Model;
private int Year;
2.There is no need for an extra variable, you can just return the car age directly:
public int GetCarAge()
{
return 2021 - Year;
}
3.The Display method creates another instance of the car class, which is wrong. you may want to return some string value about current car, or something like that:
public string display()
{
return "Make: " + Make + ", Model: " + Model + ", Year:" + Year.ToString();
//return $"Make: {Make}, Model:{Model}, Year:{Year}"; //can be written like this too
}
You have a class, but you are not using it. Lets make it a bit more meaningfull, construct an instance of a car and use it for writing:
public class Car
{
public readonly string Make;
public readonly string Model;
public readonly int Year;
public Car(string make, string model, int year)
{
Make = make;
Model = model;
Year = year;
}
public int CarAge()
{
return DateTime.Now.Year - Year;
}
}
static void Main(string[] args)
{
Console.WriteLine("Welcome to Historic Car Calculator");
Console.WriteLine("Please enter the make of your car");
var make = Console.ReadLine();
Console.WriteLine("Please enter the model of your car.");
var model = Console.ReadLine();
Console.WriteLine("Please enter the year your car was made.");
int year = Convert.ToInt32(Console.ReadLine());
var car = new Car(make, model, year);
Console.WriteLine("Your " + car.Make + " " + car.Model + " is " + car.CarAge() + " years old");
Console.ReadKey();
}
I see you have a variable for currentYear which is not needed. The current year isn't information that relates to the car. Also your function needs to return a value. So look at the sample code below for inspiration.
public class Car
{
// private fields
string make;
string model;
int year;
//Overloaded Constructor
public Car(string make, string model, int year)
{
this.make = make;
this.model = model;
this.year = year;
}
// public properties
public string Make { get { return make; } }
public string Model { get { return model; } }
public int Year { get { return year; } }
// function to calculate age
public int CarAge(int currentYear)
{
return currentYear - year;
}
// convert class to string for dsplay
public string ToString()
{
return $"{year} {make} {model} is {CarAge(DateTime.Now.Year))} years old.";
}
}
Additional functionality I included below by overriding ToString() which allows you to write things like Console.WriteLine(car) and it will display the specific information about the car. This method tells the computer how to convert the data in a class to a string representation for display or processing.
You need to be aware that Program and Car are two separate classes, even though you have declared Car inside the Program class.
In your static Main method, you need to instantiate (create) an instance (object) of the Car class with the parameters captured from your calls to Console.ReadLine():
public static void Main()
{
Console.WriteLine("Welcome to Historic Car Calculator");
Console.WriteLine("Please enter the make of your car");
var make = Console.ReadLine();
Console.WriteLine("Please enter the model of your car.");
var model = Console.ReadLine();
Console.WriteLine("Please enter the year your car was made.");
int year = Convert.ToInt32(Console.ReadLine());
// Create a Car Object using the above parameters:
// This is the proof that:
// The class should have a constructor that takes 3 parameters: make, model, year.
Car car = new Car(make, model, year);
// Deliberately demonstrate you have an Age function by
// calling the Age function on the car:
Console.WriteLine("Your car's age is {0} years old", car.Age());
// Override standard Object.ToString() to create a string representation
// Instead of creating a new method called display().
Console.WriteLine("Your Car: {0}", car);
}
You should should get into the habit of using Properties to represent the meta-data of the car, the following code will use the auto-properties syntax to declare these properties as readonly:
public string Make { get; }
public string Model { get; }
public int Year { get; }
remove currentYear... that is not needed at all.
There is a comment about an overloaded constructor... But you have not overloaded anything. At the very least you should remove that comment.
If your class needs to support serilization, then it will be important to create an additional default constructor that takes no parameters, but then the backing properties will also need to be settable from external contexts. Serilization is an important consideration for all classes in modern programming as we commonly use serilization techniques to transfer data between APIs and files, in a real world application you would want your Car instance to be saved to a database or disk, a user does not re-enter their car details, for every car, every time they use your app. This is where serialization becomes very useful, I'll include an example of this at the end of this post.
4 things about your CarAge function:
You are not using the value of Year argument at all, so remove it.
The name is redundant, we already know it is a Car Car.CarAge() is just a silly, make your code easier to read by renaming to something that includes a verb to describe what it does like CalculateAge() or perhaps just simply Age().
You do not need to create a new instance of a Car, this method is already part of a car definition, so it has access to the member variables.
This is contraversial... at least from a homework point of view, the value of 2021 should NOT be hardcoded, even though the requirement suggests this. In real world software design hardcoding time, system or environment based variables is a serious red flag
In .NET DateTime.Now will give you the current time and date, from there you can access the current year.
public int Age()
{
return DateTime.Now.Year - this.Year;
}
Finally, replace your display method with an override of the standard Object.ToString(). That is the whole purpose of the ToString() to create the standard display representation of your class.
public override string ToString()
{
return String.Format("Make: {0}, Model: {1}, Year: {2}, Age: {3}", this.Make, this.Model, this.Year, this.Age();
// You could also use string interpolation:
// return $"Make: {this.Make}, Model: {this.Model}, Year: {this.Year}, Age: {this.Age()}";
}
Overall this makes your Car class look something like this:
public class Car
{
public string Make { get; }
public string Model { get; }
public int Year { get; }
public Car(string make, string model, int year)
{
Make = make;
Model = model;
Year = year;
}
/// <summary>Override ToString to return the standard display format for a car</summary>
public override string ToString()
{
return String.Format("Make: {0}, Model: {1}, Year: {2}, Age: {3}", this.Make, this.Model, this.Year, this.Age();
// You could also use string interpolation:
// return $"Make: {this.Make}, Model: {this.Model}, Year: {this.Year}, Age: {this.Age()}";
}
/// <summary>Caculate and return the Current Age of this Car based on the current System Year and stamp Year of this car.</summary>
public int Age()
{
return DateTime.Now.Year - this.Year;
}
}
Serialization Support
As explained above, if is a good habit to get into supporting serialization, this will enable the instances of your class to be saved to text based files or to be transferred via text based protocols like HTTP. In an increasingly cloud oriented industry, you will come across this requirement very soon.
To support Serialization in a generic sense a class needs to have the following:
A parameter-less constructor, this is known as the default constructor.
The meta-data that you want to be saved and restored MUST be declared as public Properties.
This is because the default string based serialization processors will create an instance of the target class using the default constructor and will then set each of the properties one by one.
We can add the default serialization support to your Car class by making the auto-properties settable by external contexts and by adding the default constructor:
public class Car
{
public string Make { get;set; }
public string Model { get;set; }
public int Year { get;set; }
/// <summary>Create a default Car representation, meta-data will need to be set explicitly </summary>
public Car()
{
}
/// <summary>Overload to Create a Car Instance with the specified meta-data</summary>
public Car(string make, string model, int year)
{
Make = make;
Model = model;
Year = year;
}
/// <summary>Override ToString to return the standard display format for a car</summary>
public override string ToString()
{
return String.Format("Make: {0}, Model: {1}, Year: {2}, Age: {3}", this.Make, this.Model, this.Year, this.Age();
// You could also use string interpolation:
// return $"Make: {this.Make}, Model: {this.Model}, Year: {this.Year}, Age: {this.Age()}";
}
/// <summary>Caculate and return the Current Age of this Car based on the current System Year and stamp Year of this car.</summary>
public int Age()
{
return DateTime.Now.Year - this.Year;
}
}
There are other benefits to this Serialization ready implementation, we can now avoid overloaded constructors altogether with the following class instantiation syntax:
var car = new Car { Make = make, Model = model, Year = year };
Related
I was trying to understand properties better and I came across this page with this example:
https://www.tutorialspoint.com/csharp/csharp_properties.htm
using System;
namespace tutorialspoint {
class Student {
private string code = "N.A";
private string name = "not known";
private int age = 0;
// Declare a Code property of type string:
public string Code {
get {
return code;
}
set {
code = value;
}
}
// Declare a Name property of type string:
public string Name {
get {
return name;
}
set {
name = value;
}
}
// Declare a Age property of type int:
public int Age {
get {
return age;
}
set {
age = value;
}
}
public override string ToString() {
return "Code = " + Code +", Name = " + Name + ", Age = " + Age;
}
}
class ExampleDemo {
public static void Main() {
// Create a new Student object:
Student s = new Student();
// Setting code, name and the age of the student
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info: {0}", s);
//let us increase age
s.Age += 1;
Console.WriteLine("Student Info: {0}", s);
Console.ReadKey();
}
}
}
output: Student Info: Code = 001, Name = Zara, Age = 9
I don't understand how the first example is able to output the whole line written in the class "student". In the main method, we are using "s" which is an object created in the class "exampledemo". How is it able to call a method from another class?
I guess it's something related to inheritance and polymorphism (I googled the override keyword) but it seems to me that the two classes are indipendent and not a subclass of the other.
I'm a total beginner at programming and probably quite confused.
s is of type Student (as declared on the first line of Main()). Therefore one can call a method on the object to modify it or print it. When you do s.Name = "Zara"; you are already calling a method on Student to update it (technically, a method and a property are the same, they only differ by syntax).
The line Console.WriteLine("Student Info: {0}", s); is actually the same as Console.WriteLine("Student Info: " + s.ToString());. The compiler allows writing this in a shorter form, but internally the same thing happens.
Let me show a real life example.
You have a sketch of your dream bicycle. Your bicycle exists only in sketch. This is a class.
Then you are going to garage and building your bicycle from the sketch. This process can be called like creation of object
of bicycle at factory from your sketch.
According to your example, class is Student.
You are creating an object by the following line:
Student s = new Student();
Object takes space in memory. How can we read values of objects from memory? By using object reference.
s is an object reference to the newly created object type of Student.
How is it able to call a method from another class?
s is an object reference to the newly created object type of Student. So it can call any public method of this object.
I was trying to understand properties
Properties are evolution of getter and setter methods. Looking for a short & simple example of getters/setters in C#
The compiler generates a pair of get and set methods for a property, plus a private backing field for an auto-implemented property.
Are C# properties actually Methods?
I am trying to read a .txt file into a list without using a List<string> type. I have created a separate class, called Club, that does all of the sorting. However, I am having difficulties actually reading in the .txt file.
string path = "C:\\Users\\Clubs-2019";
public List<Club> ReadClubsTxtFile()
{
List<Club> outcome = new List<Club>();
string[] text = File.ReadAllLines(path);
outcome.Add(text);
return outcome;
}
The line outcome.Add(text); shows an error as I am trying to send the wrong type to the list.
This is a sample of the text file:
Club Name Club Number Meeting Address Latitude Longitude Meeting Day Meeting Time Meeting End Time
Alabaster-Pelham 4018 1000 1st St North Alabaster AL 35007 33.252414 -86.813044 Thursday 12:15 PM 1:15 PM
Albertville 4019 860 Country Club Road Albertville AL 35951 34.296807 -86.198587 Tuesday 12:00 PM 1:00 PM
Alexander City 29375 16 Broad St. Alexander City AL 35010 32.945387 -85.953948 Monday 12:00 PM 1:00 PM
The "Clubs" Class is shown below.
public Club(string name, ClubTypes type, long idNumber, RecurrableMeeting regularMeeting = null, RecurrableMeeting boardMeeting = null, List<IndividualMeeting> otherMeetings = null)
{
this.name = name;
this.type = type;
this.idNumber = idNumber;
this.regularMeeting = regularMeeting;
this.boardMeeting = boardMeeting;
this.otherMeetings = otherMeetings;
if (this.otherMeetings == null)
this.otherMeetings = new List<IndividualMeeting>();
}
"The line "outcome.Add(text); " shows an error as I am trying to send the wrong type to the list."
The reason for this error is that you're trying to add a string[] to a list that contains Club. What we need is a method that will take a string and return a Club, and then we can call that method on each file line before adding to the list.
A common way to do this is to add a Parse method to the Club class that can create an instance of the class from a string.
A complete example could be provided if you shared some sample lines from the text file (typically these would map to properties of the class) and the definition of the Club class. However, here is a sample that you can hopefully apply to your specific situation.
First, example lines in a text file:
1,Ravens,10/10/2019,2
2,Lions,05/25/2019,5.7
3,Tigers,09/12/2018,6.2
4,Bears,11/05/2019,9.1
5,Wildcats,03/04/2017,4.8
And the definition of Club
public class Club
{
public int Id {get; set;}
public string Name {get; set;}
public DateTime FoundedOn {get; set;}
public double Score {get; set;}
}
As you can see, the lines in the text file map to properties of the Club class. Now we just need to add a static method to the Club class that returns an instance of the class based on a line of text from the file.
The idea is that we split the line on the comma character, convert each part to the correct data type for the property, set the properties, and return the class. We need to validate things like:
The line is not null
The line contains the correct number of parts
Each part is the correct datatype
In the case of a validation failure, we have some common choices:
Throw an exception
Return null
Return a partially populated class
In the sample below I'm returning null to indicate bad data, mostly because it makes parsing the file easier.
Here's the class with the Parse method added:
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime FoundedOn { get; set; }
public double Score { get; set; }
public static Club Parse(string input)
{
// Try to split the string on the comma and
// validate the result is not null and has 4 parts
var parts = input?.Split(',');
if (parts?.Length != 4) return null;
// Strongly typed variables to hold parsed values
int id;
string name = parts[1].Trim();
DateTime founded;
double score;
// Validate the parts of the string
if (!int.TryParse(parts[0], out id)) return null;
if (name.Length == 0) return null;
if (!DateTime.TryParse(parts[2], out founded)) return null;
if (!double.TryParse(parts[3], out score)) return null;
// Everything is ok, so return a Club instance with properties set
return new Club {Id = id, Name = name, FoundedOn = founded, Score = score};
}
}
Now that we have the parse method, we can create a List<Club> from the text file quite easily:
public static List<Club> ReadClubsTxtFile(string path)
{
return File.ReadAllLines(path).Select(Club.Parse).ToList();
}
You could do something like this:
string path = "C:\\Users\\Clubs-2019";
public List<Club> ReadClubsTxtFile()
{
return File.ReadAllLines(path)
.Select( line => new Club {SomeProperty = line} )
.ToList();
}
I have a class called Person which has properties Name, DateOfBirthand Age. Name and DateOfBirth are supplied when class instance is created but I want the Age property to be computed automatically as soon as the instance of class is created. I know how to do it using Parametrized Constructor but as I am trying to understand the power of Properties, I attempted following program but it is not giving expected output.
using System;
namespace Property
{
class Person
{
#region private varaibles
private int _age;
#endregion
#region properties
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public int Age
{
get { return _age; }
set { _age = (DateTime.Now - DateOfBirth).Days / 365; }
}
#endregion properties
}
class Program
{
static void Main(string[] args)
{
Person P = new Person() { Name = "Merin Nakarmi", DateOfBirth = new DateTime(1990, 1, 12) };
Console.WriteLine("{0}, whose Date Of Birth is {1} is {2} years old!", P.Name, P.DateOfBirth, P.Age);
}
}
}
The output I am getting is
I am expecting age to be 28. Help please.
It seems you need a readonly age, if that's the case, you don't need to set anything in Age property, simply do:
public int Age
{
get {
return DateTime.Now.Year - DateOfBirth.Year;
}
}
You may also want to have a look here and read a bit more on properties.
By the way as also #John pointed out concerning the correct calculation of the age(meaning taking leap years into account, you may want to have a look here)
You are trying to read the property without setting it. So you can do both of these things in code like
get { return ((DateTime.Now - DateOfBirth).Days / 365); }
I have a List called
private List<Car> Cars; //Car is another class
I want to create a new list from the information in the List< Car > Cars that uses parameters to specify a year range to extracted from the Cars list and then placed into a new list called
private List<Car> getCars;
The code is as follows. Please note it is only part of a project so not all code is provided.
private List<Car> Cars;
private List<Car> getCars;
public List<Car> GetCars(int fromYear, int toYear)
{
getCars = new List<Car> { };
foreach (Car c in Cars)
if (c.Year >= fromYear && c.Year <= toYear)
getCars.Add(c);
return getCars;
}
The problem I'm having is although there are no errors showing up when I run the code the new list does not print out, instead it print's out
System.Collection.Generic.List'1[Lab__2.Car]
Any help would be great in how to make it print out the list's objects instead of what is above. Finally My lecturer has specified that he wants the method formatted as such
public List<Car> GetPrices(int year)
{
}
What you're seeing is the output you get when you call print directly on a list. It won't automatically print the contents, you must print each item yourself.
You are surely calling the ToString() method directly on the List but it will only print its type:
Default implementations of the Object.ToString method return the fully qualified name of the object's type. - MSDN
So, you must iterate through the items in the list and print it's details. Example:
foreach (Car c in Cars) {
Console.WriteLine(c.Name); //I do not know what properties you have in the class Car. Change accordingly.
}
Or you can use String.Join():
String.Join(Environment.NewLine, Cars); //You can change 'Environment.NewLine' to ", " if you want a comma instead of a new line.
You can print the items of the list by iterating over them, and printing each object individually:
for (Car car in Cars)
{
Console.WriteLine(car.ToString);
}
Doing this Console.WriteLine(Cars) will only give you information about the List object, usually being the full type and maybe the address in memory, depending on the runtime.
If you want to print the items of the list after getting them from a method, do this:
for (Car car in GetCars(fromYear, toYear))
{
Console.WriteLine(car.ToString);
}
When printing an object that is not a string (or other character sequence) you might want to override the ToString method that is inherited from Object in order to specify the information of the object (or Car in this case) you want to print.
Try something along these lines:
public Class Car
{ // guessing here
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public override string TosString()
{
return "Make: " + Make + ", Model: " + Model + ", Year: " + year;
}
And then, somewhere in your program:
foreach(var car in CarList.Where(c => c.Year >= fromYear && c.Year <= toYear))
{
Console.Out.WriteLine(car);
}
Note how the functionality of your GetCars() can be expressed in a fairly readable Linq Where method call applied to the list.
As #Ciara specified, you must print each item yourself. When you issue something like Console.WriteLine(car) in your code, the Console class automatically calls ToString() method on your Car object.
The documentation for ToString() method on MSDN specifies:
The default implementation of the ToString method returns the fully qualified name of the type of the Object [...]
In your case, that name is System.Collection.Generic.List'1[Lab__2.Car]. To change what ToString() method returns, in your Car class override the method:
public class Car
{
public string Make { get; set; }
public int Year { get; set; }
public override string ToString()
{
return String.Format("Make: {0}, Year: {1}", Make, Year);
}
}
Afterwards you need to iterate the list and print each item:
carList.ForEach(Console.WriteLine);
I'm working through an example in the bookPro C# and the .NET Platform and I'm making a mistake somewhere that I can't see. The program compiles and runs, but the Manager object in this example isn't having the right value of 'StockOptions' returned. In an effort of concision, I'm going to try to only post the relevant code because this example is all about class hierarchies and there's like six different classes. The virtual method GiveBonus in the Employee class isn't being correctly overridden in the Manager class.
class Manager : Employee
{
private int numberOfOpts;
//the properties are inherited from Employee
public int StockOptions { get; set; }
//***METHODS*** this is returns the StockOptions amount as it is in the
// constructor, there's no logic being applied
public override void GiveBonus(float amount)
{
base.GiveBonus(amount);
Random r = new Random();
numberOfOpts += r.Next(500);
}
public override void DisplayStats()
{
base.DisplayStats();
Console.WriteLine("you have {0} stock options", StockOptions);
}
public Manager() { }
public Manager(string fullName, int age, int empID, float currPay,
string ssn, int numbofOpts) : base(fullName, age, empID, currPay, ssn)
{
ID = empID;
Age = age;
Name = fullName;
Pay = currPay;
StockOptions = numbofOpts;
}
}
snippet from my Main() method
Manager chucky = new Manager("chucky", 50, 92, 100000, "333-33-3333", 9000);
chucky.GiveBonus(300);
chucky.DisplayStats();
Console.WriteLine();
I made a mistake while asking the question. What I should have asked is why I have to use
Console.WriteLine("you have {0} stock options", numbOfOpts);
instead of
Console.WriteLine("you have {0} stock options", StockOptions);
It's not meant to add a random number to 9000 - it's meant to give a random number of stock options as well as the "base" pay bonus:
public override void GiveBonus(float amount)
{
base.GiveBonus(amount);
Random r = new Random();
// Note numberOfOpts, not currPay
numberOfOpts += r.Next(500);
}
Unfortunately, as we've got two separate fields - one created by an automatically implemented property - it won't actually update the value of StockOptions... it's not clear whether this is due to your editing, or whether it's a mistake in the book. (There are various other things I dislike about this code, but hey...)