Click here to Skip to main content
15,885,914 members
Articles / Programming Languages / C#

Property Acccesor Modifiers

Rate me:
Please Sign up or sign in to vote.
3.00/5 (6 votes)
3 Oct 2005CPOL10 min read 35K   9   1
General description of the new property accessor modifiers in C# 2.0.

A brief introduction to accessors

Properties, commonly known as 'smart fields' in C# community, are nothing but built-in extensions of internal data fields. As we all know, Encapsulation is one of the three pillars of object oriented programming; the other two being Inheritance and Polymorphism. In C#, data encapsulation is possible through either classes or structs, and by using various access modifiers like private, public, protected, internal etc., it is possible to control the accessibility of class members.

By default, internal members not explicitly declared with less restriction will be private, which makes them accessible to only their containing class. With respect to class level fields, it is usually wise to maintain this restriction and access their data through controlled entry points. This is a good programming practice, since the data fields are not directly accessible outside the class. This can, of course, be done using methods, referred to in this vein as accessors; which return the data in a given field back to the caller. The example below illustrates this concept.

C#
public class User
{
    string username, password;

    public string GetUserName()
    {
        return this.username;
    }

    public string GetPassword()
    {
        return this.password;
    }
}

Here, we see a class User with two fields: username and password, that presumably need some external visibility beyond class scope. The methods GetUserName() and GetPassword() provide read-only access to the data held in these fields. Another reasonable assumption is that a user might at some point need to have their password changes. To facilitate this, the user class might be written to allow a new accessor which would allow write access to the internal field password. The example below illustrates this:

C#
public class User
{
    string username, password;

    public void GetUserName()
    {
        return this.username;
    }

    public void GetPassword()
    {
        return this.password;
    }

    public void SetPassword(string password)
    {
        this.password = password;
    }
}

Above, the class User has a new method SetPassword added to it which takes a single string argument that simply overwrites the previous value of password.

Scenarios such as this can beg the question against encapsulation since, at least in this instance (and not counting whatever validations and business logic might exist in GetPassword and SetPassword), the combination of GetPassword and SetPassword provide the exact same functionality as making password public. There are however certain cases that validate the strategy. Internal class fields might represent aggregates of another class in a one-to-many relationship for instance. Under that condition, direct access to the field instance could be dangerous since the instance could be overwritten. The actual requirement in these cases is the ability to traverse the symbolic link into the collection of aggregate class instances. Imagine, in the case of the User class, that the User object represents a banking user who might have multiple accounts associated with the membership, each of which would have a list of all transactional activities performed by that user over a period of time. The solution could be implemented by adding a Hashtable field to the User class and the ability to search the user's accounts for a given account and return an activity list for it. The example below illustrates this.

C#
public class User
{
    string username, password;
    Hashtable accounts;

    public void GetUserName()
    {
        return this.username;
    }

    public void GetPassword()
    {
        return this.password;
    }

    public void SetPassword(string password)
    {
        this.password = password;
    }

    public Activity GetActivityList(string accountNumber)
    {
        return (ActivityList)accounts[accountNumber];
    }
}

Assuming an ActivityListing class, the example above searches the accounts field for an account number and returns the activity listing associated with it.

Properties the old way

C# introduced the concept of properties as built-in shorthand for the scenarios highlighted in the previous section. The properties exist only in the language level, and are later converted by the compiler into the appropriate getter and setter methods when it parses the C# property syntax. The full User class described in the last example could also be implemented like this:

C#
public class PropertyBasedUser
{
    string username, password;
    Hashtable accounts;

    public string UserName
    {
      get
      {
        return this.username;
      }
    }

    public string Password
    {
      get
      {
         return this.password;
      }
      set
      {
         password = value;
      }
    }

    public Hashtable Accounts
    {
      get
      {
        return accounts;
      }
    }
}

Remember that a property should have at least one accessor, either set or get. The set accessor has a free variable available in it called value, which gets created automatically by the compiler. We can't declare any variable with the name value inside the set accessor.

Using Hashtable as the return type of the property and providing only a get accessor restricts consumers from overwriting the association, but it does still have its drawbacks. Consumers are free to add and remove accounts to the user via the methods of the all purpose Hashtable class. Needless to say, this could have unforeseeable consequences. C# also provides the concept of indexed properties. In this case, an indexer could be implemented to restrict the association to only searching the aggregate instances. The example below illustrates this:

C#
public class Accounts
{
    Hashtable accounts;

    public ActivityList this[string accountNumber]
    {
      get
      {
        return (ActivityList)accounts[accountNumber];
      }
    }
}

public class PropertyBasedUser
{
    string username, password;
    Accounts accounts;

    public string UserName
    {
      get
      {
        return this.username;
      }
    }

    public string Password
    {
      get
      {
        return this.password;
      }
      set
      {
        password = value;
      }
    }

    public Accounts UserAccounts
    {
      get
      {
        return accounts;
      }
    }
}

In the example above, the Accounts property name has been changed to UserAccounts, and rather than returning a Hashtable instance, it returns a reference to the Accounts field. The accounts Hashtable has been moved to the Accounts class, and a new accounts field of type Accounts has takes its place. The Accounts class defines an indexer which simply searches a Hashtable for the user's activity list. A user's activity listing could now be accessed from the caller as follows:

C#
PropertyBasedUser user = new PropertyBasedUser();
ActivityList list = user.UserAccounts["1234567890"];

Just like with the accessors we defined in the previous section, the built in C# accessors allow multiple instructions before returning the control back to the caller. They can even throw exceptions. However, since normal data fields and properties are stored in the same memory space, in C#, it is not possible to declare a field and property with the same name.

C# also supported static properties, which belong to the class rather than to any instances of the class. Except for the fact that indexers are not allowed to be static, all rules applicable to a static member are applicable to static properties also. The code below illustrates the use of static properties.

C#
static int userCount;

public static int UserCount
{
  get
  {
    return userCount;
  }
}

Accessing the UserCount field is as simple as the following:

C#
int userCount = PropertyBasedUser.UserCount;

C# also allows the properties of a base class to be propagated down the inheritance hierarchy to applicable derived classes in the same manner as any appropriately declared member. Assume a user with a non traditional account, perhaps an investment type account. This user could be defined in a class which would inherit from PropertyBasedUser. The example below illustrates.

C#
public class FinancialServicesUser : PropertyBasedUser
{
}

Properties defined in PropertyBasedUser can be accessed through FinancialServicesUser in a consuming object as follows:

C#
FinancialServicesUser fuser = new FinancialServicesUser();
ActivityList flist = fuser.UserAccounts["123456789"];
int fuserCount = FinancialServicesUser.UserCount;

The above program is very straightforward. The inheritance of properties is just like inheritance of any other member.

Enhancements

One of the main problems with the old approach to properties in C# presents itself once accessibility and polymorphism become issues. The problem is simple. Whereas in general property definition we can define accessors with whatever access requirements we want, using properties, we have no direct access to the visibility of the individual accessors within the definition. In the section entitled: A brief description of properties, we introduced a User class which utilized self-defined accessors. The definition of this class is provided below.

C#
public class User
{
    string username, password;

    public string GetUserName()
    {
        return this.username;
    }

    public string GetPassword()
    {
        return this.password;
    }
}

I just can't tell you how many times I've created a class like this:

C#
public class Test
{
    int age = 0;
    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
        }
    }
}

And wanted to allow only classes inside of my assembly to have the ability to modify age. As it stands, I have created a Test class with a field age which can be read or modified by everyone. Because C# at the time did not allow accessors to be modified, I had to do something like this:

C#
public class Test2
{
    int age = 0;
    public int Age
    {
        get
        {
            return age;
        }
    }

    internal void SetAge(int i)
    {
        this.age = i;
    }
}

In version 1.0 of C#, not specifying the set accessor makes the property read only, hence preventing it from being modified. In order to change the value associated with this property, we have to create a method SetAge, which we could then be set to internal to ensure that it is visible to only types inside of the assembly. Well, wouldn't you know that C# 2.0 allows you to do just that! So rather than the code above, you could implement the same class like this:

C#
public class Test3
{
    int age = 0;
    public int Age
    {
        get
        {
            return age;
        }
        internal set
        {
            age = value;
        }
    }
}

Notice that in the example above, the internal modifier has been applied to only the set accessor of the Age property, and that the method SetAge has been removed. Accessors without modifiers will default to the modifier defined for their property. The above example can now be used as follows:

C#
class Program
{
    static void Main(string[] args)
    {
        Test3 test = new Test3();
        test.Age = 55;  //processes the set accessor
    }
}

The accessor will allow protected, internal, private, protected internal. But there are some rules that govern implementing them:

  1. They may not be used in an interface or in an explicit interface member implementation.
  2. For a property or indexer that has no override modifier, they are permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors.
  3. For a property or indexer that includes an override modifier, an accessor must match them, if any, of the accessor being overridden.
  4. They must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. To be precise:
  5. If the property or indexer has a declared accessibility of public, any accessor modifier may be used.
  6. If the property or indexer has a declared accessibility of protected internal, the accessor modifier may be either internal, protected, or private.
  7. If the property or indexer has a declared accessibility of internal or protected, the accessor modifier must be private.
  8. If the property or indexer has a declared accessibility of private, no accessor modifier may be used.

In the following example, a new class Test4 has been added which inherits from Test3. Test3 has been modified so that the set accessor modifier is protected not internal. This declaration ensures that only sub classes of Test3 or methods inside Test3 can modify the age property. A new property BaseAge does just this.

C#
public class Test3
{
    int age = 0;
    public int Age
    {
        get
        {
            return age;
        }
        protected set
        {
            age = value;
        }
    }
}

public class Test4 : Test3
{
    public int BaseAge
    {
        get
        {
            return this.Age;
        }
        set
        {
            this.Age = value;
        }
    }
}

We now create a driver to test the functionality. The code is listed below:

C#
class Program
{
    static void Main(string[] args)
    {
        Test4 test = new Test4();
        test.BaseAge = 44;  //works fine

        Test3 t3 = new Test3();
        t3.Age = 55;  //Error set accessor is not available
    }
}

Compiling the code above will generate an error. This is because Age can only be accessed from within Test3 or base classes of Test3.

Suppose we decided to make the Age property virtual so that base classes could override it and change its functionality. There is no difference in version 2.0 from the previous version regarding implementing this. The code below shows the modified Test3 class:

C#
public class Test3
{
    protected int age = 0;
    public virtual int Age
    {
        get
        {
            return age;
        }
        protected set
        {
            age = value;
        }
    }
}

If you are planning to use the base class functionality and not override Age, then you have no problem. If you do decide to override the base class functionality, then you must ensure that the sub class accessor modifiers match those of the base class. The example below illustrates this concept:

C#
public class Test5 : Test3 
{
    public override int Age
    {
        get
        {
            return this.GetAgeFromDB();
        }
        protected set
        {
            this.SetDBAge(value);
        }
    }

    int GetAgeFromDB()
    {
        int tempage = 0;
        //read value from db and store in temp age
        return tempage;
    }

    void SetDBAge(int age)
    {
        //save age to database
    }
}

As you can see from the above example, the class Test5 has overridden the Age property, replacing the base functionality, which simply stores the values passed into age in a filed variable, and returns that variable back to the caller when the get accessor is invoked, with a new version which writes and reads the value from a database. The implementation is, of course, stubbed out, so please don't try to access the database by using //read value from db and store in temp age, then claim that Edward Moemeka told you to. If the protected modifier is not included in the set accessor of the Age property, the code will not compile.

One final thing before we are done with this topic. Say, we create an interface which defines the Age property as follows:

C#
public interface ITest
{
   int Age { get;set;}
}

We could then create a class Test6 which implements this interface as follows:

C#
public class Test6 : ITest
{
    int age;
    int ITest.Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
        }
    }
}

Based on the interface definition of ITest, we could not apply any accessor modifiers to Test6.Age. In short, an accessor that is used to implement an interface cannot have a modifier associated with it. If, however, only one accessor was defined in the interface, hence only one accessor is required to implement the interface, the other accessor can be declared with a modifier. The example below illustrates this concept:

C#
public interface ITest
{
   int Age { get;}
}

public class Test6 : ITest
{
    int age;
   public int Age
    {
        get
        {
            return age;
        }
        internal set
        {
            age = value;
        }
    }
}

Please note that this will only work when the interface implementation does not explicitly reference the interface. If Age were defined as follows:

C#
public class Test6 : ITest
{
   int age;
   int ITest.Age
    {
        get
        {
            return age;
        }
        internal set
        {
            age = value;
        }
    }
}

The code will not compile.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Hi I'm Edward Moemeka,
For more interesting articles about stuff check out my blog at http://moemeka.blogspot.com
To correspond, email me at edward.moemeka@synertry.com
To support my company, thus help me feed my family, check out our awesome online preview at www.synertry.com. Remember, its in alpha Wink | ;-)

Comments and Discussions

 
GeneralMy vote of 5 Pin
Júlio César de Carvalho19-Jul-10 8:02
Júlio César de Carvalho19-Jul-10 8:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.