Oct 3, 2008

Something that a lot of C# developers will miss when designing their classes

The thing is that the developers often forget they can set access modificators at set / get level for a property.

Consider the following classes:

namespace Locals
{
  
class Program
   {
    
static void Main(string[] args)
     {
        
LocalManager LocalMgr = new LocalManager();
        Remote.
RemoteManager RemoteMgr = new Remote.RemoteManager();
        
User usr = new User();

        LocalMgr.Promote(usr);
        
Console.WriteLine("After local promotion: " + usr.Occupation);
     }
   }

  
public class LocalManager
   {
    
public LocalManager() { }
    
public void
style="color:Black;"> Promote(User usr)
     {
        usr.Occupation =
"Chief Officer";
     }
   }

  
public class User
   {
    
private string strOccupation;
    
public User() { }
    
public string Occupation
     {
        
get { return this.strOccupation; }
        
set { this.strOccupation = value; }
     }
   }
}

Here, after the program is executed the Local manager will promote the user and the output will be :
After local promotion: Chief Officer

Now imagine that we want to have the name of the manager who promoted the User, so we can store it in a database. Obviously we need to have an instance of the Manager class, and write the information in the database, prior changing the occupation for the user.

We will want to have a method in the User class, which takes the Manager instance, writes everything needed in the database and then changes the Occupation.
But we can't be sure that all developers will use the method and not the property directly.

Firs let's add a property to the LocalManager class to hold the name of the manager. Then I will create a method to change users occupation, which will take an instance of LocalManager. This will be the only way an external class to change the occupation of a user. After this, we will slightly modificate the code in the main program so everything is called properly. Here is the code:

using System;
using System.Collections.Generic;
using System.Text;

namespace Locals
{
  
class Program
   {
    
static void Main(string[] args)
     {
        
LocalManager mgr = new LocalManager();
        mgr.ManagerName =
"Pavel Donchev";
        
User usr = new User();
        mgr.Promote(usr);
        
Console.WriteLine("After promotion: " + usr.Occupation);
     }
   }

  
public class LocalManager
   {
    
private string strManagerName = string.Empty;

    
public LocalManager() { }
    
public void Promote(User usr)
     {
        usr.Promote(
this, "Chief Officer");
     }
    
public string ManagerName
     {
        
get { return this.strManagerName; }
        
set { this.strManagerName = value; }
     }
   }

  
public class User
   {
    
private string strOccupation;
    
public User() { }
    
public string Occupation
     {
        
get { return this.strOccupation; }
        
private set { this.strOccupation = value; }
     }
    
public void Promote(LocalManager manager, string Occupation)
     {
        
if (null != manager && !string.IsNullOrEmpty(manager.ManagerName))
        {
          
// Do additional work here, for example write the
           // name of the manager in the database amoung with
           // time stamp of the promotion to have audit...

          
this.Occupation = Occupation;
        }
        
else
        {
          
throw new Exception("A valid manager instance and occupation should be passed!");
        }
     }
   }
}

Now our classes are ready. You can give your user class away and be sure that no one can modify the property without passing a valid manager instance (actually the sequence of exceptions will guide your peers to follow the correct logic ;).


Oh, one more thing - we didn't proof that no one can assign the User.Occupation property directly. Well try to do so, you should receive compiler error message:

The property or indexer 'Locals.User.Occupation' cannot be used in this context because the set accessor is inaccessible

No comments: