I took up C#/.NET programming a few years ago, and the learning curve was not too terribly rough given my experience with C, C++ and (a little bit) Java. But as I immerse myself in a larger project, I keep finding things that I like about this language.
From time to time I'll touch on some of these features here, mainly for experienced developers who may not have visited C# yet. Today I'm writing about properties.
In C++ or Java, one creates an object with member fields and methods, and it's generally considered poor object-oriented form to expose the fields directly to users of the object (i.e., they should be private or protected). The better mechanism is to create get/set methods that allow manipulation of these private fields, and they can enforce restrictions on the variables and insure that internal state is consistent.
Delphi, the Pascal derivative, supported a feature known as Properties, which allowed an object's method to create get/set methods that look exactly to the callers like regular fields in an object - it was just the best of both worlds. Since the author of Delphi also created C#, it should be no surprise that this useful feature has made it into his new creation.
Properties allow creation of code blocks associated with explicit get and set fragments, and the system calls them as appropriate when used those contexts.
A contrived example lifted from the Microsoft knowledge base:
public class TimePeriod { public double Seconds; // REAL FIELD public double Hours // PROPERTY { get { return Seconds / 3600; } set { Seconds = value * 3600; } } } ... TimePeriod t = new TimePeriod(); t.Seconds = 60; // sets the real field Console.WriteLine("Time: {0} Hours or {1} Seconds", t.Hours, t.Seconds); t.Hours = 24; // sets the property Console.WriteLine("Time: {0} Hours or {1} Seconds", t.Hours, t.Seconds);
Here, setting Hours is done explicitly with the real underlying field, but Seconds are computed with the get fragment. Then, in the next step when t.Seconds are set — as if it were a real field — the computation is done by the set fragment to modify the underlying t.Hours field.
We certainly could have defined explicit get/set functions to do this:
public class TimePeriod { public double Seconds; public double getHours() { return Seconds / 3600; } public void setHours(double h) { Seconds = h * 3600; } } ... TimePeriod t = new TimePeriod(); t.Seconds = 60; Console.WriteLine("Time: {0} Hours or {1} Seconds", t.getHours(), t.Seconds); t.setHours(24); Console.WriteLine("Time: {0} Hours or {1} Seconds", t.getHours(), t.Seconds);
... but this treats Seconds and Hours differently when they don't need to be; using a property obviates the need to use unnatural setHours/getHours mechanisms when they can simply be treated as a regular field.
Properties can be effectively readonly by simply omitting the set part:
public class ShoppingItem { public double price; public int quantity; public double total { get { return price * quantity; } // no set property! } }
Here, total computes (item x quantity) to get the total, but there's simply no way to set that value. As before, one could have written this as a simple function public double total(), but once you've started using properties, it gets pretty attractive.
In many cases all you really want to do is provide a simple wrapper to underlying fields, without any validation checks or computations - this leaves the door open to adding those checks later as needed:
public class Foo { // tedious private int _something; public int something { get { return _something; } set { _something = value; } } ... others added here }
Most will say that this seems tedious, and having to write all this just to add a new fields is not that better than explicit getSomething/setSomething methods. But C# 3.0 provides a shortcut to this very case:
public class Foo { public int something { get; set; } // easy business ... others added here }
Because get and set are provided without bodies, the runtime simply creates an internal variable that it operates on with the obvious behavior. If something ever needs more definition, one can include the body to get or set without changing any of the calling code.
This is so easy to use, and so flexible when you do decide to add logic later, that it becomes almost habit to use properties for nearly everything public.
Properties can be public, private, or any other access specifier, and the get/set can have separate access classes. This example shows the property as a whole being public (which applies to get), but the more restrictive protected specifier for the set limits setting to derived classes only:
public class Foo { // only derived classes public int something { /* public */ get; protected set; } ... others added here }
Properties can be part of interfaces, they can specifi get/set parts separately (i.e, one interface can require public int foo { get; } while another can require public int foo { set; }, and a single implementation of get/set will satisfy them both.
It does not take long at all to really get to love properties and wish they were available in C++.
Comments