How to fix override ToString method - c#

I am setting up a class with a GUI and having issues on this mandatory ToString method I need in my code. I missed 2 classes due to traveling for a family emergency and now just a little lost on what exactly I am doing here.
Honestly, I don't understand much of whats going on, so I am looking for an explanation. But I have tried watching videos and moving around the code to no avail.
class Sandwich
{
public string name = "Tony";
public string meat = "None";
public int tomatoSlices = 1;
public override tomatoSlices.ToString()
{
public double ComputerPrice()
{
return 4.0 + (0.5 * tomatoSlices);
}
}
}
The program should run, but not sure why it isnt. It has something to do with the tomatoSlices integer, I suppose.

As mentioned in the comments you have declared a method inside another method. You need to move the ComputerPrice() outside of the ToString method. Also, you need to remove the tomatoSlices from the ToString definition:
class Sandwich
{
public string name = "Tony";
public string meat = "None";
public int tomatoSlices = 1;
public double ComputerPrice()
{
return 4.0 + (0.5 * tomatoSlices);
}
public override string ToString()
{
return ComputerPrice().ToString();
}
}
Now, when you call sandwich.ToString() it will return the value of ComputerPrices() as a string e.g.:
var sandwich = new Sandwich();
var price = sandwich.ToString();

It's a little hard to tell exactly what you want, but you have a method nested inside another method, which is not legal.
Probably you want to move ComputerPrice() out of the ToString() method, and then implement what you want for ToString() (which is typically the string representation of the class).
Also, you don't specify tomatoSlices as part of the method name; when you override a base class method, you just use the base class method name, which is ToString(). You also have to declare a return type of string for the method.
Other things you may want to do are:
Use public properties instead of fields
Use PascalCase for public members
Use the decimal datatype for working with currency (to avoid rounding errors)
Here's a sample class that addresses all these issues:
class Sandwich
{
public string Name { get; set; }
public string Meat { get; set; }
public int TomatoSlices { get; set; }
public Sandwich()
{
Name = "Tony";
Meat = "None";
TomatoSlices = 1;
}
public override string ToString()
{
return $"This sandwich named {Name} " +
$"has {Meat} meat and {TomatoSlices} slices of " +
$"tomato, for a total cost of {ComputerPrice()}";
// Or just return the price if that's what you want instead:
// return ComputerPrice().ToString();
}
public decimal ComputerPrice()
{
return 4M + 0.5M * TomatoSlices;
}
}

Related

How to refactor an abstract class that has public members/properties for each concrete type?

I am looking at some legacy code and have come across an abstraction that has properties for each of its derived/concrete types. I cannot share the exact code but please imagine that instead of it being a simple operation that there are numerous operations that are much more complex.
I have not come across anything like this before and have a lot of questions? First, is this a pattern that I am not aware of? If so, what is it? Second question, how should I refactor this so that it follows solid principles?
I will try my best to come up with a better example if needed.
public enum ToolType
{
Unknown = 0,
HRMonitor,
Dumbell,
SomeForceDevice
}
public abstract class ToolData
{
private ToolData()
{
IsValid = false;
this.ToolType = ToolType.Unknown;
}
public ToolData(ToolType toolType)
{
this.ToolType = toolType;
}
public ToolType ToolType { get; }
public virtual bool IsValid { get; protected set; } = true;
public double LinkQuality { get; set; }
public NullToolDataValue NullData => this as NullToolDataValue;
public DumbellDataValue DumbellData => this as DumbellDataValue;
public HeartRateDataValue HRData => this as HeartRateDataValue;
public SomeForceDataValue SomeForceData => this as SomeForceDataValue;
}
public class NullToolDataValue : ToolData
{
public NullToolDataValue() : base(ToolType.Unknown)
{
IsValid = false;
}
}
public class DumbellDataValue : ToolData
{
public double WeightValue { get; private set; }
public DumbellDataValue(double weightValue) : base(ToolType.Dumbell)
{
this.WeightValue = weightValue;
}
public override string ToString()
{
return WeightValue.ToString(CultureInfo.InvariantCulture);
}
}
public class HeartRateDataValue : ToolData
{
public int HeartRate { get; private set; }
public HeartRateDataValue(int heartRate) : base(ToolType.HRMonitor)
{
this.HeartRate = heartRate;
}
public override string ToString()
{
return HeartRate.ToString(CultureInfo.InvariantCulture);
}
}
public class SomeForceDataValue : ToolData
{
public double LeftHandForceValue { get; private set; }
public double RightHandForceValue { get; private set; }
public int LeftHandPosition { get; private set; }
public int RightHandPosition { get; private set; }
public SomeForceDataValue(double lefthandValue, double rightHandValue, int leftHandPosition, int rightHandPosition) : base(ToolType.SomeForceDevice)
{
this.LeftHandForceValue = lefthandValue;
this.LeftHandPosition = leftHandPosition;
this.RightHandForceValue = rightHandValue;
this.RightHandPosition = rightHandPosition;
}
public override string ToString()
{
return $"{LeftHandForceValue.ToString(CultureInfo.InvariantCulture)}" +
$"| {LeftHandPosition.ToString(CultureInfo.InvariantCulture)}" +
$"| {RightHandForceValue.ToString(CultureInfo.InvariantCulture)}" +
$"| {RightHandPosition.ToString(CultureInfo.InvariantCulture)}";
}
}
It is being used/consumed via something like the below which it too is missing some inheritance and things for brevity:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data?.DumbellData == null) return;
//add value to some collection
Collection.Add(data.DumbellData.WeightValue);
}
}
public class HRExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data?.HRData == null) return;
//add value to some collection
Collection.Add(data.HRData.HeartRate);
}
}
Okay, I'm going to give a shot at answering - hopefully this will help.
First up, ToolData shouldn't contain any References/Enums/whatever that list its subtypes. So first on the chopping block: all the lambda properties that cast the object as a specific subtype. I can kinda understand the appeal - you know an instance of ToolType happens to be a FloobieTool, so you call instance.FloobieTool and magically get a FloobieTool cast. But... well, there are problems that come with it, not the least is that you're breaking Opened/Closed Principle. Nothing wrong with making the person calling the class cast it explicitly with (FloobieTool)instance if they know they're working with a FloobieTool.
Next up: the ToolType. Why do you need this? You can tell if your instance of ToolData is a FloobieTool by simply doing an 'is' check in an IF condition:
void SomeFunc(ToolData toolData)
{
if (!(toolData is FloobieTool)) throw new Exception("Non-Floobie!");
// more code
}
I mean, what does that enumeration actually get you? Because it has a definite cost: it has to be kept in-sync with the list of classes that implement ToolData.
Also, the part in ToolDataReceived() for each of those Exercise classes seems... weird. I mean, you've got an exercise, and you're passing in ToolData. Why are you storing the amount of the Dumbell exercise? As opposed to just storing the ToolData. I mean, you're going through quite a bit of testing/casting/etc, just to add the dumbbell weight to a Collection. Any reason you can't just store the ToolData instance and call it a day? If you really need to specifically store Dumbbell info, you could do something like:
public class DumbbellExercise
{
List<DumbbellDataValue> dumbbellData = new List<DumbbellDataValue>();
public void AddToolData(ToolData toolData)
{
if (toolData is DumbbellDataValue)
this.dumbbellData.Add((DumbbellDataValue)toolData);
}
}
Hopefully that helps - it's tough to go into too many details when we're working off an abstracted example of your actual problem :-)
Having seen your edits, I believe even more firmly that the way to refactor this code is to use pattern matching. Pattern matching requires at least C# 7.0 so I'll include an almost-as-good way to do it pre-7.0 versions.
Step 1
Mark the properties obsolete using ObsoleteAttribute and pass true for the error parameter.
[Obsolete("Use pattern matching instead.", true)]
public NullToolDataValue NullData => this as NullToolDataValue;
[Obsolete("Use pattern matching instead.", true)]
public DumbellDataValue DumbellData => this as DumbellDataValue;
[Obsolete("Use pattern matching instead.", true)]
public HeartRateDataValue HRData => this as HeartRateDataValue;
[Obsolete("Use pattern matching instead.", true)]
public SomeForceDataValue SomeForceData => this as SomeForceDataValue;
This will make it a compiler error to use them in any code processed by the compiler. If you're doing any reflection on them, you'll get a runtime exception instead (after step 3 is complete) if you don't also change that code.
Step 2
Modify every call site that uses those properties to use pattern matching instead. If all you're doing is what you showed in the question, is should be as simple as this:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data is DumbellDataValue dumbell)
Collection.Add(dumbell.WeightValue);
// OR
if (!(data is DumbellDataValue dumbell))
return;
Collection.Add(dumbell.WeightValue);
}
}
The second variation is not as pretty because the condition has to be parenthesized before it can be negated (hey, at least VB has the IsNot keyword; go figure) but you get the same early return that the existing code has.
It looks like you're using at least C# 6.0 because you're using the null-coalescing operator (?.), but if you're not using at least 7.0, you can do this, instead:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
DumbellDataValue dumbell = data as DumbellDataValue;
if (dumbell != null)
Collection.Add(dumbell.WeightValue);
// OR
DumbellDataValue dumbell = data as DumbellDataValue;
if (dumbell == null)
return;
Collection.Add(dumbell.WeightValue);
}
}
Step 3
Remove the properties. If there are no more compiler errors, the properties aren't being used, so you're free to get rid of them.
Additional Note
The IsValid property has a strange duality to it. It can be assigned by the derived classes but it's also virtual so it can be overridden, too. You really should pick one. If it were my decision, I'd keep it virtual and make it read-only.
public abstract class ToolData
{
// Continue to assume it's true...
public virtual bool IsValid => true;
}
public class NullToolDataValue : ToolData
{
// ...and indicate otherwise as needed.
public override bool IsValid => false;
}

C# Abstract methods, readonly properties

I have an assignment (bunch of oop stuff, polymorphism and inheritance) and amongst other things I have to do the following:
I need to add an abstract method to the class Vehicle (called calculateOccupancy()) which has to return the % of the leftover space in a vehicle. I then have to implement that in my derived classes. The issue here is, I have 3 derived classes, two of them have 2 attributes and one has 3. So how do I make my abstract method, so that it can accept 2 or 3 arguments.
I have to add a unchangeable property to the class Person, and the property has to return the first letter of the name and surname, divided by a dot.
namespace Example
{
abstract class Vehicle
{
//class member variables, most likely unnecessary for the questions
private Person driver;
private string vehicleBrand;
private string vehicleType;
private double fuelConsumption;
private double gasTankSize;
private string fuelType;
//the default constructor
public Vehicle()
{}
//The abstract method from question 2
// how to make it so that it wont error when I need to
//put in 3 variables instead of two, meaning, how would I add int c
public abstract double calculateOccupancy (int a, int b);
//The derived class that implements the method
class Bus : Vehicle
{
private int allSeats;
private int allStandingSeats;
private int busPassengers; //the number of passengers
//the constructor
public Bus (int a, int b, int c)
{
allSeats=a;
allStandingSeats=b;
busPassengers=c;
}
//the abstract method
// needs to take in int b (standing seats)
public override double calculateOccupancy(int a, int c)
{
//this code calculates the leftover space in the vehicle
double busSpace=(busPassengers*100) / allSeats;
return busSpace;
//same code for the leftover standing space (int c)
}
}
}
class Person
{
protected string name;
protected string lastName;
//question 1
//properties for char gender
protected char gender;
//question 3
protected readonly string initials;
//the code errors, at the get/set
public char Gender
{
get{ return gender; }
set {gender=value;}
}
/*and the property im not sure how to make
public string Initials{}
*/
}
I hope the comments add some clarity, rather than confusion, thank you for your help everybody.
Assumption going forward - I threw some of your variable names into Google Translate and it seems to be Slovenian. I'm assuming that going forward which helped me make some clarity of what your code does.
1) Replace - If you already have a variable that is a char representing spol then I believe you're supposed to use the new enum type you are to create to represent it.
public enum Spol
{
Moski = 0,
Zenska = 1
}
Change:
protected char spol;
public char Spol
{
get{ return spol; }
set {spol=value;}
}
To: public Spol Spol { get; set; }
2) Defaults & Conditions - Use int c = 0 as your 3rd parameter and use a formula/algorithm that ignores it if it is the default value.
3) Getters - This property doesn't have a setter and therefore cannot be changed (directly).
public string GiveThisAName
{
get
{
if (String.IsNullOrWhiteSpace(ime))
{
return null;
}
if (String.IsNullOrWhiteSpace(priimek))
{
return null;
}
return ime[0] + '.' + priimek[0];
}
}
Notes
1) Heavily recommend making the parameters of your capacity function (i.e. izracunajZasedenost(int a, int b)) to be named something useful (i.e. a name descriptive of what they do) other than a and b.
2) For the record, #1 seems more like an appropriate question for your instructor, teacher, or whoever gave you this assignment.
Give the "optional" values a value when you create the abstract method
public abstract double izracunajZasedenost (int a = -1, int b = -1)
{
if (a == -1){
//do method with ignoring a
}
};

For what do you need "this." in c# [duplicate]

This question already has answers here:
When do you use the "this" keyword? [closed]
(31 answers)
Closed 5 years ago.
The older apprentices in my company use "this." a lot.
Two weeks ago I started coding object-oriented and still don't get for what it is being used.
You need to understand what instance is first. Let's say you have an object:
public class House
{
public decimal Height { get; set; }
}
You can have multiple instances of it:
var smallHouse = new House { Height = 100M };
var bigHouse = new House { Height = 300M };
Each instance has its own value of Height. When you want to work with Height in a method of House, you need to refer to the current instance method is operating at (the one consumer called).
This can be done explicitly by using this as a special kind of variable that refers to this current instance:
public class House
{
public decimal Height { get; set; }
public bool IsItTooBig()
{
return this.Height > 200;
}
}
Or you can omit this and let C# guess that what you mean is the instance value:
public class House
{
public decimal Height { get; set; }
public bool IsItTooBig()
{
return Height > 200;
}
}
Programmers differ in opinion whether it's good or bad to be explicit there. If you follow capitalization conventions, you can distinguish instance state and method scope state (normal variables) by it.
There are cases where you absolutely need it, for example when you have naming conflict, or when you want to return current instance from a method:
public class House
{
public decimal Height { get; set; }
public House AddFloor()
{
Height += 100;
return this;
}
}
You should consider applying immutability in many of these cases though.
The keyword 'this' represents the instance of an object used to explicitly call a method, field or property of that instance.
Commonly used when your private fields have the same name as the parameters in a given method:
private string name;
public void SetName(string name) {
this.name = name;
}
When you want to refer to instance field within that class you use this, it can be omitted but there are cases it can not be omitted.
public class InstanceClass
{
int field = 10;
public void Method()
{
int field = 0;
Console.WriteLine(field); // outputs 0
Console.WriteLine(this.field); // outputs 10 because "this" refers to field.
}
}
if there is no declared local variable that conflicts with field name, "this" can be omitted.
public class InstanceClass
{
int _field = 10;
public void Method()
{
int field = 0;
Console.WriteLine(field);
Console.WriteLine(_field); // prefixed with _.
// no conflicts
// so "this" can be omitted.
}
}
another case where you can not omit this, is when you use indexer.
public class InstanceClass
{
private List<int> _source;
private int offset;
public int this[int index] // you use "this"
{
get => _source[index + offset]
set => _source[index + offset] = value;
}
public void Method()
{
var first = this[0]; // must use "this" to refer to indexer for this class.
}
}
"this" is used for calling constructor overloads too.
public class Foo
{
public Foo() : this(0)
{
Console.WriteLine("world");
}
public Foo(int param1)
{
Console.WriteLine("Hello");
}
}
//...
var foo = new Foo(); // outputs "Hello world"
"this" also refers to instance of class itself. so if you want to return instance of self you use this.
public class Foo
{
public Foo ReturnMe() // weird example.
{
return this;
}
}

C# can an inherited method or property use the derived class members without creating a new method?

I'm coding a simple game using C# to help me learn basic object oriented concepts.
In this code below:
class entity
{
int hp;
string name;
public entity()
{
hp = 1;
name = "entity";
}
public string status()
{
string result;
result=name + "#" + " HP:" + hp;
return result;
}
class dragon : entity
{
new public string name;
new int hp;
public dragon()
{
hp = 100;
name = "Dragon";
}
}
I made an object for "Dragon" as such
dragon mydragon = new dragon();
The problem is with the following code:
mydragon.status();
This returns a string but with the "name" and "hp" of the entity class object (i.e. hp=1, name=entity).
I'd like to have this return the dragon object's values (hp=100, name=dragon). I'm not sure what I'm doing wrong but it seems dead simple.
After fiddling and struggling for hours, the only solution I could come to was to simply copy & paste the status() method over to the dragon class. But I'm sure there's a better way to do this.
Many thanks in advance.
Simply decorate fields hp and name in the class entity with protected access modifier. With that, they will be available to the dragon class as well and you won't have to redefine them. You can keep the dragon's constructor as it is, since it will run after the constructor in entity class, thus overriding values of its fields.
It could look like the following:
public class Entity
{
protected int hp;
protected string name;
public Entity()
{
hp = 1;
name = "entity";
}
public override string ToString()
{
string result = name + "#" + " HP:" + hp;
return result;
}
}
public class Dragon : Entity
{
public Dragon()
{
hp = 100;
name = "Dragon";
}
}
It is customary for names of classes in C# to begin with uppercase letter. Also, for stuff like returning string representation of a class, the ToString() method is usually overriden.
I'd add virtual keyword in entity class to the status method and override in each inherited class.
edit: Nikola's code looks also very fair if you want to use just .ToString() instead of Status()
Make the following changes...
class entity
{
protected int hp;
protected string name;
...
class dragon : entity
{
// new public string name; - you're creating new variables hiding the base ones
// new int hp; - ditto. Don't need them
....

C# Return Value, But keep getting error. Why?

Hello fellow stackoverflow members!
I'm very new to the C# language transfer from Java, Obj-C.
It looks pretty same as Java, but I have trouble issue in very simple thing.
I have created two individual class files, Class-A and Class-Human.
Specification for Class-A
it contains the static main method declared.And I've tried to create the new instance of Class-Human.
public static void main(String args[])
{
Human human = new Human("Yoon Lee", 99);
int expected = human.getNetID; //<-gets the error at this moment.
}
Specification for Class-Human
namespace Class-A
{
public class Human
{
public String name;
public int netId;
public Human(String name, int netId)
{
this.name = name;
this.netId = netId;
}
public int getNetID()
{
return netId;
}
}
Why can't copy over into local variable?
The compiler prompts me the error of
'Cannot convert method group of 'getNetID' delegate blah blah'
Thank you.
Change the method-call to:
int expected = human.getNetID();
In C#, method-calls require parantheses () containing a comma-separated list of arguments. In this case, the getNetID method is parameterless; but the empty parantheses are still required to indicate that your intention is to invoke the method (as opposed to, for example, converting the method-group to a delegate-type).
Additionally, as others have pointed out, there is a mismatch betweem the return-type of the method and the variable you're assigning its value to, which you're going to have to resolve somehow (change both the field-type and method return-type to int / parse the string as an integer, etc.).
On another note, C# natively supports properties for getter-setter semantics, so the idiomatic way of writing this would be something like:
//hyphens are not valid in identifiers
namespace ClassA
{
public class Human
{
// these properties are publicly gettable but can only be set privately
public string Name { get; private set; }
public int NetId { get; private set; }
public Human(string name, int netId)
{
this.Name = name;
this.NetId = netId;
}
// unlike Java, the entry-point Main method begins with a capital 'M'
public static void Main(string[] args)
{
Human human = new Human("Yoon Lee", 99);
int expected = human.NetId; // parantheses not required for property-getter
}
}
}
You're trying to use a method as if it's a property. You need parenthesis and to convert the string to int, or just make getNetID return an int.
I think you meant:
public int getNetID()
{
return netId;
}
Or better still, use automatic properties:
public int NetId {get; private set;} //Notice Making N in Net capital
And then:
int expected = human.getNetID();
This will do the trick (-:
It should be human.getNetID()
Edit: And yes, as Oren says - you should change your netId getter to return int. I assume that is what you want to do.
I see that netId is integer.
getNetID() return type is string.
return type is not matching.
netID is declared as an Int:
public int netId;
but your function getNetID returns a string:
public String getNetID()
Therefore, the body of getNetID makes no sense when it tried to return an int as a string:
return netId;
Human human = new Human("Yoon Lee", 99);
int expected = human.getNetID(); //<-gets the error at this moment.
you need to add parentheses after the method call.
The way you have it right now you are fetcing the function itself.

Categories

Resources