Properties


Another type of class member is the property. A property combines a field with the methods that access it. As some examples earlier in this book have shown, often you will want to create a field that is available to users of an object, but you want to maintain control over the operations allowed on that field. For instance, you might want to limit the range of values that can be assigned to that field. While it is possible to accomplish this goal through the use of a private variable along with methods to access its value, a property offers a better, more streamlined approach.

Properties are similar to indexers. A property consists of a name along with get and set accessors. The accessors are used to get and set the value of a variable. The key benefit of a property is that its name can be used in expressions and assignments like a normal variable, but in actuality the get and set accessors are automatically invoked. This is similar to the way that an indexer’s get and set accessors are automatically used.

The general form of a property is shown here:

 type name {     get {         // get accessor code     }     set {         // set accessor code     } }

Here, type specifies the type of the property, such as int, and name is the name of the property. Once the property has been defined, any use of name results in a call to its appropriate accessor. The set accessor automatically receives a parameter called value that contains the value being assigned to the property.

It is important to understand that properties do not define storage locations. Thus, a property manages access to a field. It does not, itself, provide that field. The field must be specified independently of the property.

Here is a simple example that defines a property called MyProp, which is used to access the field prop. In this case, the property allows only positive values to be assigned.

 // A simple property example. using System; class SimpProp {   int prop; // field being managed by MyProp   public SimpProp() { prop = 0; }   /* This is the property that supports access to      the private instance variable prop.  It      allows only positive values. */   public int MyProp {     get {       return prop;     }     set {       if(value >= 0) prop = value;     }   } } // Demonstrate a property. class PropertyDemo {   public static void Main() {     SimpProp ob = new SimpProp();     Console.WriteLine("Original value of ob.MyProp: " + ob.MyProp);     ob.MyProp = 100; // assign value     Console.WriteLine("Value of ob.MyProp: " + ob.MyProp);     // Can't assign negative value to prop     Console.WriteLine("Attempting to assign -10 to ob.MyProp");     ob.MyProp = -10;     Console.WriteLine("Value of ob.MyProp: " + ob.MyProp);   } }

Output from this program is shown here:

 Original value of ob.MyProp: 0 Value of ob.MyProp: 100 Attempting to assign -10 to ob.MyProp Value of ob.MyProp: 100

Let’s examine this program carefully. The program defines one private field, called prop, and a property called MyProp that manages access to prop. As explained, a property by itself does not define a storage location. A property simply manages access to a field. Thus, there is no concept of a property without an underlying field. Furthermore, because prop is private, it can be accessed only through MyProp.

The property MyProp is specified as public so that it can be accessed by code outside of its class. This makes sense because it provides access to prop, which is private. The get accessor simply returns the value of prop. The set accessor sets the value of prop if and only if that value is positive. Thus, the MyProp property controls what values prop can have. This is the essence of why properties are important.

The type of property defined by MyProp is called a read-write property because it allows its underlying field to be read and written. It is possible, however, to create read-only and write-only properties. To create a read-only property, define only a get accessor. To define a write-only property, define only a set accessor.

You can use a property to further improve the fail-soft array class. As you know, all arrays have a Length property associated with them. Up to now, the FailSoftArray class simply used a public integer field called Length for this purpose. This is not good practice, though, because it allows Length to be set to some value other than the length of the fail-soft array. (For example, a malicious programmer could intentionally corrupt its value.) We can remedy this situation by transforming Length into a read-only property, as shown in the following version of FailSoftArray:

 // Add Length property to FailSoftArray. using System; class FailSoftArray {   int[] a; // reference to underlying array   int len; // length of array -- underlies Length property   public bool errflag; // indicates outcome of last operation   // Construct array given its size.   public FailSoftArray(int size) {     a = new int[size];     len = size;   }   // Read-only Length property.   public int Length {     get {       return len;     }   }   // This is the indexer for FailSoftArray.   public int this[int index] {     // This is the get accessor.     get {       if(ok(index)) {         errflag = false;         return a[index];       } else {         errflag = true;         return 0;       }     }     // This is the set accessor     set {       if(ok(index)) {         a[index] = value;         errflag = false;       }       else errflag = true;     }   }   // Return true if index is within bounds.   private bool ok(int index) {    if(index >= 0 & index < Length) return true;    return false;   } } // Demonstrate the improved fail-soft array. class ImprovedFSDemo {   public static void Main() {     FailSoftArray fs = new FailSoftArray(5);     int x;     // can read Length     for(int i=0; i < fs.Length; i++)       fs[i] = i*10;     for(int i=0; i < fs.Length; i++) {       x = fs[i];       if(x != -1) Console.Write(x + " ");     }     Console.WriteLine();     // fs.Length = 10; // Error, illegal!   } }

Length is now a property that uses the private variable len for its storage. Length defines only a get accessor, which means that it is read-only. Thus, Length can be read, but not changed. To prove this to yourself, try removing the comment symbol preceding this line in the program:

 // fs.Length = 10; // Error, illegal!

When you try to compile, you will receive an error message stating that Length is read-only.

Although the addition of the Length property improves FailSoftArray, it is not the only improvement that properties can make. The errflag member is also a prime candidate for conversion into a property since access to it should also be limited to read-only. Here is the final improvement of FailSafeArray. It creates a property called Error that uses the original errflag variable as its storage.

 // Convert errflag into a property. using System; class FailSoftArray {   int[] a; // reference to underlying array   int len; // length of array   bool errflag; // now private   // Construct array given its size.   public FailSoftArray(int size) {     a = new int[size];     len = size;   }   // Read-only Length property.   public int Length {     get {       return len;     }   }   // Read-only Error property.   public bool Error {     get {       return errflag;     }   }   // This is the indexer for FailSoftArray.   public int this[int index] {     // This is the get accessor.     get {       if(ok(index)) {         errflag = false;         return a[index];       } else {         errflag = true;         return 0;       }     }     // This is the set accessor     set {       if(ok(index)) {         a[index] = value;         errflag = false;       }       else errflag = true;     }   }   // Return true if index is within bounds.   private bool ok(int index) {    if(index >= 0 & index < Length) return true;    return false;   } } // Demonstrate the improved fail-soft array. class FinalFSDemo {   public static void Main() {     FailSoftArray fs = new FailSoftArray(5);     // use Error property     for(int i=0; i < fs.Length + 1; i++) {       fs[i] = i*10;       if(fs.Error)         Console.WriteLine("Error with index " + i);     }   } }

The creation of the Error property has caused two changes to be made to FailSoftArray. First, errflag has been made private because it is now used as the underlying storage for the Error property. Thus, it won’t be available directly. Second, the read-only Error property has been added. Now, programs that need to detect errors will interrogate Error. This is demonstrated in Main( ), where a boundary error is intentionally generated, and the Error property is used to detect it.

Property Restrictions

Properties have some important restrictions. First, because a property does not define a storage location, it cannot be passed as a ref or out parameter to a method. Second, you cannot overload a property. (You can have two different properties that both access the same variable, but this would be unusual.) Finally, a property should not alter the state of the underlying variable when the get accessor is called. Although this rule is not enforced by the compiler, violating it is wrong. A get operation should be nonintrusive.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net