Properties
The Crash Course
- Properties provide a quick and effective approach to creating getters and setters for instance variables.
- You can create a property with code like the following:
public int Score { get { return score; } set { score = value; if (score < 0) { score = 0; } } }
- Not all properties need both a setter and a getter.
- Getters and setters can have different access modifiers.
- Auto-implemented properties can be created that allow you to quickly define a simple property with default behavior (public int Score { get; set; }).
Introduction
In the previous tutorial, we talked about how it is generally considered a good idea to make your fields private and make methods that allow access to fields as needed. But we also saw how having a bunch of GetWhatever() and SetWhatever(…) methods are not as pretty as direct field access, like thing.whatever.
We're going to address that problem here by discussing a rather unique feature of C# called properties.
The intent of properties is to give us the best of both worlds: the ability to protect our data from arbitrary changes from the outside world without sacrificing simple field-like access.
Consider this class definition:
class Rectangle { private float _width; private float _height; public float GetWidth() { return _width; } public float SetWidth(float width) { _width = width; } public float GetHeight() { return _height; } public float SetHeight(float height) { _height = height; } public float GetArea() { return _width * _height; } public Rectangle(float width, float height) { _width = width; _height = height; } }
This class has made its fields private to ensure it can enforce any rules it may have about them, but it comes with a cost. Consider this code that makes a rectangle and then tries to add 1 to the original width.
Rectangle rectangle = new Rectangle(5, 6); rectangle.SetWidth(rectangle.GetWidth() + 1);
That second line is especially annoying compared to its counterpart if we just made our fields public, which would otherwise look more like this:
Rectangle rectangle = new Rectangle(5, 6); rectangle._width++;
Rather than having to pick between the two, we can use a special C# language feature called a property, which allows us to define getter and setter methods in a compact way that supports field-like access without sacrificing the protection methods offer.
Adding Basic Properties
A property is easier to show than describe, so let's just jump into it and replace our GetWidth() and SetWidth(float) methods with a property:
class Rectangle { private float _width; private float _height; public float Width { get { return _width; } set { _width = value; } } public float GetHeight() { return _height; } public float SetHeight(float height) { _height = height; } public float GetArea() { return _width * _height; } public Rectangle(float width, float height) { _width = width; _height = height; } }
A property has an accessibility modifier, which is often (but not always) public, followed by a type (float in this case), then the name of the property. Inside of curly braces is a definition for a getter, using the get keyword, and a setter, using the set keyword. The getter is expected to return something with a type matching the property's type. The setter must not return anything but has access to the special value value, which is the value the outside world intended to set.
For all practical purposes, this property is identical to the GetWidth and SetWidth methods that it replaced, but does have a couple of advantages.
First, it directly ties the getter and setter together. With two random methods, you could spread them out across the class, making it hard to recognize they belong together, and you could give them mismatched names like GetWidth() and SetLength(float). With a property, the two share a name.
Second, on the outside, a property is accessed the same way you'd access a field:
Rectangle rectangle = new Rectangle(5, 6); rectangle.Width++; // or... rectangle.Width = 20; Console.WriteLine(rectangle.Width);
Convenient, isn't it?
Behind the scenes, the property still compiles down to two methods, so we still get all of the benefits we were hoping for by making our fields private and accessing them through public methods.
Variations on the Basic Concept
Properties have been in the C# language since the start, but it feels like every new version of C# adds a new extra thing you can do with properties. That leaves you with a ton of nuance and possibilities with properties. We won't cover them all here, but we'll cover a few common examples.
Get- or Set-Only Properties
Properties do not need to have both a getter and a setter. If you want one without the other, you just leave the one you don't need off. We can us that to make a property for the rectangle's area:
public float Area { get { return _width * _height; } }
Different Accessibility Levels
If you want a property getter to be public but the setter to be private, you can do that by applying the public keyword to the property but the private keyword to the part you want restricted:
public float SomeProperty { get { return _someValue; } private set { _someValue = value; } }
Auto-Properties
Perhaps the single most popular variation on the idea of a property is that of an auto-property.
Our rectangle width is extremely simple. If we gather all of the elements that support the rectangle's width, it is these:
private float _width; public float Width { get { return _width; } set { _width = value; } }
Notably, there are no actual rules to enforce here! There are no side effects and no validation. It is just a simple "stick the value into the related field" and "retrieve the value currently in the related field."
This is not uncommon, especially when a class is relatively new. (Classes tend to grow extra validation and complexities after they've been around for a while.) To reduce the amount of boilerplate code you have to write, the following is an auto-property that is precisely equal to the code before:
public float Width { get; set; }
The compiler will automatically generate a related field called the backing field and wire it all up to get and set that field as expected. This allows you to reserve the option to turn it into a full property later on without affecting most of the program. You will see a ton of auto-properties when you look at others' code. They're everywhere.
Object Initializer Syntax
Another cool thing about properties is that there's special syntax to set a bunch of properties on a newly created object. While the constructor will typically demand you supply values for anything mandatory, this syntax makes it possible to set a bunch of optional properties while the object is still being created:
Player player1 = new Player("CatBot 2000") { Score = 2000, LivesLeft = 9 };
The alternative would have been to assign values to those properties on subsequent lines. This version is more compact and confines all initialization code to a single line.
What's Next?
That covers all of the important things about properties.
The next thing we want to go over is something very similar in appearance (but actually quite different in implementation) to a class: a struct.
thanks for the tutorials, they are very helpful. I came from c++ and I wanted to learn this language because I was told that the dev type for games was faster.
Development times in C# will usually be much faster than C++. (Though naturally, it depends on the skill level of the C# programer and the C++ programmer.) At any rate, I'm glad you're here, and good luck making games!
By far the best programming tutorials I have ever read. Thank you.
Thanks, Sean!
I am confused about something. If properties don't need instance variables, then why use instance variables at all? In your book, under the using properties try it out, you don't make instance variables for several colors, instead you made properties. Should I be skipping (private) instance variables and using properties instead? Thanks.
Auto-properties do technically have an instance variable as a backing field, even though you don't have direct access to it. The C# compiler creates one.
Private instance variables with no associated property, auto properties, and your own properties with a specific named private instance variable as a backing field all have their place, and they're all used quite regularly.
Use just a private instance variable when you don't want anybody to have access to it. (When it's just implementation details, as opposed to being part of the class's public interface (I'm using "interface" loosely here, not referring specifically to C# interfaces, though they're conceptually related).
Use an auto-property when the data is a part of the public API/interface of the class (when "outsiders" need to be able to access that piece of data) but when there is no special logic required to validating the data. (Auto properties don't give you an opportunity to run any sort of logic when the backing field is getting set.)
Use a regular property with your own instance variable backing field when some custom logic needs to be ran every time it gets set (or someone attempts to set it). This might be making sure the value being set is within a certain range, or raising an event when it gets set or something.
One other option is to just use a plain old method. The syntax of a property gives the impression that you're just setting a value in a variable, which executes really fast. With a property, there's always going to be a little extra logic involved, so it's not quite as fast. (At the very least, there's the overhead of calling the property.) A little extra logic isn't a problem, but if your setter (or getter) requires a lot of extra work, it would be worth considering just using a normal named method for getting and/or setting a piece of data. With a property, there's this illusion that it's just writing to a variable and will be fast. Even though it's always a little more than just writing to a variable, people expect it to be fast. If it's not, making it a method instead (which people don't necessarily assume will be fast) will come out feeling more natural to users of your class.
Hope that clarifies it a bit.
Just let me thank you for these incredible tutorials. You have earned yourself a book sale when I'm done here!
Ben
Post preview:
Close preview