Lesson 9: Using Properties
Lesson 9 starts with the code as it was at the end of Lesson 8.
Source code after the Lesson 9 changes: http://www.bluerosegames.com/xna101/lessons/XnaLesson9.zip
C# provides a nice alternative to accessing Fields directly from outside the class using something called Properties which are more like a method call, but look to other objects like public Fields.
The benefits of Properties over direct access to Fields include but are not limited to:
- Sanity checks, the object can check if a value is valid before it accepts the change.
- Ability to combine Field values to make things easier for the calling object.
- The underlying structure of the data can be changed without requiring changes to the calling object.
- Properties can be made read only or write only.
- Easier to accommodate future needs of the class.
There are also some negatives:
- Fields inside value type properties are read only. You have to create a new copy of the entire value type (like Vector2) to set a new value. With fields, you can just say Position.X = 10, with properties you would have to do Position = new Vector2(10, Position.Y);
- Properties cause a small performance hit. a bit more code has to run compared to accessing/setting a field.
- They can make your code a bit more verbose impacting readability.
There are also some more advanced benefits having to do with packaging a class library as an assembly and maintaining compatibility across versions.
For game programming, the use of properties can be a hotly debated topic, but Shawn Hargreaves who I highly respect falls on the side of using properties only when necessary. So consider this more of a "how to use properties" than a "you should always do this".
In the case of the BouncingBall class, there are 2 public Fields that we will convert to Properties. Here is a sample property definition which can replace the previously public Field Position:
private Vector2 _position;
public Vector2 Position
{
get
{
return _position;
}
set
{
_position = value;
}
}
Notice the "get" and "set" blocks. If you remove the "set" block, the property would be read only. Likewise, if you remove the "get" block, the property would be write only. Since _position is declared private, other objects will not be able to access the value of _position directly, only through the property Position. Go ahead and replace the declaration of Velocity as follows:
private Vector2 _velocity;
public Vector2 Velocity
{
get
{
return _velocity;
}
set
{
_velocity = value;
}
}
In the "set" block, notice that there is a reference to a field named value. This is a special keyword that is used when setting a property and always contains the value that is being assigned to the property. It has the same data type as the property definition, or in this case, Vector2. So in the case of the BouncingBall class, if we had the following line in our Game1 class:
ball2.Velocity = new Vector2(10, 5);
then in the Property setter for Velocity, "value" would contain a Vector2 object with X of 10 and Y of 5.
In most classes, the methods of the class will probably deal directly with the private Fields instead of going through the property logic, although in some cases it will make sense to access the data through the Properties as well, if some of the same reasons listed above apply to these values. In the BouncingBall class, it probably makes sense to access the Fields directly from inside the class, so in the Update method and the constructor, change all references to the properties to the Field names instead.
So change the BouncingBall.Update() method to use the private Fields instead of the public Properties:
public void Update()
{
// TODO: Add the BouncingBall update logic here
_position = _position + _velocity;
if (_position.X < 0 || _position.X > GraphicsViewport.Width - Texture.Width)
{
// If we get in here, we've hit a vertical wall
_velocity.X = -_velocity.X;
_position.X = _position.X + Velocity.X;
}
if (_position.Y < 0 || _position.Y > GraphicsViewport.Height - Texture.Height)
{
// If we get in here, we've hit a horizontal wall
_velocity.Y = -_velocity.Y;
_position.Y = _position.Y + _velocity.Y;
}
}
So run the program again, and it should behave exactly as it did before the changes.
So it's really up to you and up to the situation. Use Properties if they help you with whatever you're trying to accomplish. In most cases, you can change a Field to a Property later if you need to.