I've been working through this video: https://www.youtube.com/watch?v=GhQdlIFylQ8&t=11038s
I am working through "Getters & Setters" at 3:54:60 (other subjects for C# are in the description for any needed context).
This is a class that I've made in order to print to the Console as well as a "Getter and Setter".
class Song
{
private string title;
public string artist;
public int duration;
public Song(string aTitle, string aArtist, int aDuration)
{
title = aTitle;
artist = aArtist;
duration = aDuration;
}
public string Title
{
get { return title; }
set {
if (value == "Hello")
{
value = "ERROR";
} else
{
title = value;
}
}
}
}
And this is the "Main" sort of code to print out the titles of two Song
objects: "hello" and "kashmir".
class Program
{
static void Main(string[] args)
{
Song hello = new Song("Hello", "Adele", 400);
Song kashmir = new Song("Kashmir", "Green Day", 200);
Console.WriteLine(hello.Title);
Console.WriteLine(kashmir.Title);
Console.ReadLine();
}
}
However, I've tried to experiment with the "Getters and Setters" by seeing how I could print out something other than the song titles.
When I run the program, it prints out Hello
and Kashmir
both on separate lines.
How do I get it to print ERROR
or something other than the song titles (or what other ways could I do this)?
There's a sorta funny thing going on here that even seasoned devs occasionally trip over...and it's missing the difference between a backing field and a property. This happens from time to time...and absolutely baffles the dev until they realize they've made a Visual Studio intellisense-assisted error. It's doubly likely if you name your backing field the same as the property name...differing only in case.
class Song
{
private string title; //--> this is the "backing field" for the property "Title"
public string artist;
public int duration;
public Song(string aTitle, string aArtist, int aDuration)
{
//title = aTitle; //--> this only sets the backing field...not the "Title" property
Title = aTitle; //--> this sets your "Title" property
artist = aArtist;
duration = aDuration;
}
The next issue is that, in a property set
, the value
is the incoming suggested value for the property. You don't have to accept it. Your job in the property set
is to validate the incoming value, and put it into the backing field if you like it...or maybe put something else in if you don't. Changing the incoming value
is rarely what you want to do. You almost got it right...here's the commented fix:
public string Title
{
get { return title; }
set {
if (value == "Hello")
{
//value = "ERROR"; //--> this only changes the incoming value
title = "ERROR"; //--> this sets your backing field
} else
{
title = value;
}
}
}
}
Having said all that, it's not at all uncommon to see the constructor set only backing fields, rather than going through the sometimes-expensive validating code of the property set
. A lot of times, the property is the public API for the users of your type to set the value...while the constructor is kept internal or private and is only used to fill in values that had been saved off somewhere..like in a database. A good idea? Maybe/maybe not. As you've just discovered, bypassing the property set
can leave you with something that might be an error.