Example: Employees And Managers


For this example, assume that you are writing an application that does some sort of processing on data that pertains to company employees. You are not really going to worry about what sort of processing this involves — you are more interested in the fact that this means it will be quite useful to write a C# class (or a Visual Basic class module) that represents employees. You are assuming that this will form part of a software package that you can sell to companies to help them with their salary payments and so on.

The Visual Basic Employee Class Module

The following code represents an attempt to code an Employee class module in Visual Basic. The class module exposes two public properties, EmployeeName and Salary, as well as a public method, GetMonthlyPayment, that returns the amount the company needs to pay the employee each month. This isn't the same as the salary, partly because the salary is assumed to be the salary per year, and partly because later on you want to allow for the possibility of adding more money to what the company pays its employees (such as performance-related bonuses):

'local variable(s) to hold property value(s) Private mStrEmployeeName As String 'local copy Private mCurSalary As Currency 'local copy Public Property Let Salary(ByVal curData As Currency)    mCurSalary = curData End Property Public Property Get Salary() As Currency    Salary = mCurSalary End Property Public Property Get EmployeeName() As String    EmployeeName = mStrEmployeeName End Property Public Sub Create(sEmployeeName As String, curSalary As Currency)    mStrEmployeeName = sEmployeeName    mCurSalary = curSalary End Sub Public Function GetMonthlyPayment() As Currency    GetMonthlyPayment = mCurSalary/12 End Function

In real life you'd probably be writing something more complex than this, but this class suffices for the purpose here. In fact, you already have a problem with this Visual Basic class module. Most people's names do not change very often, which is why the EmployeeName property is read-only. That still requires you to set up the name in the first place. This is done using a Create method, which sets the name and the salary. That means that the process of creating an employee object looks like this:

Dim Britney As Employee Set Britney = New Employee Britney.Create "Britney Spears", 20000

This is workable but messy. The problem is that you have to write a separate initialization method, Create, instead, and hope that everyone writing client code will always remember to call it. This solution is awkward, because it doesn't make any sense to have an Employee object lying around that doesn't have a name and a salary set. However, that is exactly what you have in this code for the brief instant between instantiating Britney and initializing the object in the code. As long as you always remember to call Create, you won't run into any problems, but there is a potential source of bugs here.

In C# the situation is completely different. In C# you are able to supply parameters to constructors. All you need to do is make sure that when you define your C# Employee class, the constructor takes the name and salary as parameters. Then you can write:

 Employee Britney = new Employee("Britney Spears", 20000.00M);  

This is a lot neater and less prone to bugs. Of course, you could overload the constructor to only supply a name, for example. By the way, note the "M" appended to the salary. This is because the C# equivalent to the Visual Basic Currency type is called decimal, and "M" appended to a number in C# indicates you want the number interpreted as a decimal. You don't have to supply it, but it makes for a useful extra compile-time check.

The C# Employee class

Here is the first definition of the C# version of Employee (note that this example only shows the class definition, not the containing namespace definition):

 class Employee { private readonly string name; private decimal salary; public Employee(string name, decimal salary) { this.name = name; this.salary = salary; } public string Name { get { return name; } } public virtual decimal Salary { get { return salary; } set { salary = value; } } public decimal GetMonthlyPayment() { return salary/12; } public override string ToString() { return "Name: " + name + ", Salary: $" + salary.ToString(); } } 

Working through this code, you first see a couple of private variables, the so-called member fields corresponding to the member variables in the Visual Basic class module. The field name is marked readonly. Roughly speaking, this ensures that this field must be set when an Employee object is created and cannot subsequently be modified. In C# it isn't usual to use Hungarian notation for the names of variables, so they are called simply name and salary, rather than mStrEmployeeName and mCurSalary.(Hungarian notation means that you prefix the names of variables with some letters that indicate their type [mStr, mCur, and so on]. This type of notation is not considered important nowadays, because editors are more sophisticated and can supply automatic information about data types. Hence, the recommendation is not to use Hungarian notation in C# programs.)

The Employee class also contains a constructor, a couple of properties (Name and Salary), and two methods (GetMonthlyPayment() and ToString()). All of these are discussed next.

Note

Note that the names of the properties Name and Salary differ only in case from the names of their corresponding fields. This isn't a problem, because C# is case-sensitive. The way the properties and fields are named here corresponds to the usual convention in C# and shows how you can actually take advantage of case sensitivity.

The Employee constructor

Following the field declarations in the previous code, you have a "method" that has the same name as the class, Employee. This tells you that it is a constructor. However, this constructor takes parameters and does the same thing as the Create method in the Visual Basic version. It uses the parameters to initialize the member fields:

 public Employee(string name, decimal salary)  {  this.name = name;  this.salary = salary;  } 

There's a potential syntax problem, because the obvious names for the parameters are the same as the names of the fields: name and salary. But you've resolved this problem using the this reference to mark the fields. You could have given the parameters different names instead, but the way you've done it is still clear enough and means that the parameters keep the simple names that correspond to their meanings. It's also the conventional way of dealing with this situation in C#.

The precise meaning of the readonly qualifier on the name field can now be explained:

 private readonly string name; 

If a field is marked as readonly, the only place in which it may be assigned to is in the constructor to the class. The compiler will raise an error if it finds any code in which you attempt to modify the value of a readonly variable anywhere except in a constructor. This provides a very good way of guaranteeing that a variable cannot be modified after it has been set. It wouldn't be possible to do anything like this in Visual Basic because Visual Basic doesn't have constructors that take parameters, so class-level variables in Visual Basic have to be initialized via methods or properties that are called after the object has been instantiated.

Incidentally, this constructor doesn't just allow you to supply parameters to initialize an Employee object: it actually forces you to do so. If you tried to write the following code, it would not compile:

 Employee Britney = new Employee(); // will not compile now 

The compiler would raise an error because, in C#, a constructor must always be called when a new object is created. However, you have not supplied any parameters, and the only constructor available requires two parameters. Therefore, it is simply not possible to create an Employee object without supplying any parameters. This provides a good guarantee against bugs caused by uninitialized Employee objects!

It is possible to supply more than one constructor to a class so that you get a choice of what sets of parameters you want to pass in when you create a new object of that class. You'll see how to do this later in the chapter. However, for this particular class, your one constructor is quite adequate.

Properties of Employee

You next come to the properties Name and Salary. The C# syntax for declaring a property is very different from the corresponding Visual Basic syntax, but the basic principles are unchanged. You need to define two accessors to respectively get and set the values of the property. In Visual Basic, these are syntactically treated like methods, but in C# you declare the property as a whole and then define the accessors within the definition of the property:

 public decimal Salary { get { return salary; } set { salary = value; } } 

In Visual Basic, the compiler knows that you are defining a property, because you use the keyword Property. In C# this information is conveyed by the fact that the name of the property is followed immediately by an opening brace. If you were defining a method, this would be an opening parenthesis signaling the start of the parameter list; in the case of a field, this would be a semicolon, marking the end of the definition.

Note also that the definitions of the get and set accessors do not contain any parameter lists. That's because you know that Salary is a decimal and that the get accessor will return a decimal and take no parameters, while the set accessor will take one decimal parameter and return void. For the set accessor, this parameter is not explicitly declared, but the compiler always interprets the word value as referring to it.

Note

Once again, the syntax for defining properties shows how C# syntax is more compact, and can save you a fair bit of typing!

If you want to make a property read-only, you simply omit the set accessor, as you have done for the Name property:

 public string Name { get { return name; } } 

Methods of Employee

The example also includes two methods: GetMonthlySalary() and ToString().

GetMonthlySalary() requires little explanation, because you have seen most of the relevant C# syntax already. It simply takes the salary and divides it by 12 to convert it from an annual to a monthly salary, and returns the result:

 public decimal GetMonthlyPayment()  { return salary/12;  } 

The only new piece of syntax here is the return statement. In Visual Basic, you specify a return value from a method by setting a dummy variable that has the same name as the function to the required value:

GetMonthlyPayment = mCurSalary/12

In C#, you achieve the same result by appending a parameter to a return statement (without parentheses). return in C# also specifies that you are exiting from the function, so the C# statement

 return salary/12; 

is actually equivalent to the following Visual Basic code:

GetMonthlyPayment = mCurSalary/12  Exit Function

ToString() is slightly more interesting. In most cases, when you write a C# class, it is a good idea to write a ToString() method that can be used to get a quick view of the contents of an object. As mentioned before, ToString() is already available because all classes inherit it from System.Object. However, the version in System.Object simply displays the name of the class, not any data in the class instance. Microsoft has already overridden this method for all the numeric data types (int, float, and so on) to display the actual value of the variable, and it's quite useful for you to do the same in your classes. If nothing else, it can be a useful way of seeing the contents of an object when you are debugging:

 public override string ToString() { return "Name: " + name + ", Salary: $" + salary.ToString(); } 

The override here simply displays the name and the salary of the employee. One new piece of syntax is that you have declared the method as override. C# requires that you mark method overrides in this way; it will raise a compilation error if you don't. This eliminates the risk of any potential bugs that might lead you, for example, to accidentally override an existing method by that name without realizing it.

You have now completed writing the Employee class in both Visual Basic and C#, and so far, although there is a bit of awkwardness about constructing and initializing an Employee instance in the Visual Basic version, both languages have coped reasonably well with your requirements. However, one of the aims of this appendix is to show you why C# can be so much more powerful than Visual Basic in some situations. So it's about time you started seeing some useful C# code where it would be very difficult if not impossible to achieve the same result using Visual Basic. Let's start with a static field and property.

Static Members

It's been mentioned a few times that in C# classes can have special methods referred to as static methods, which can be called without instantiating any objects. These methods do not have any counterpart in VB6. In fact, not only methods but also fields, properties, or any other class member can be static.

Important

The term "static" has a very different meaning in C# from its meaning in Visual Basic.

To illustrate how static members work and why you would use them, imagine that you would like your Employee class to support retrieving the name of the company that each employee works for. Now there is an important difference here between the company name and the employee name, in that each employee object represents a different employee, and therefore needs to store a different employee's name. This is the usual behavior for variables in class modules in Visual Basic, and the default behavior for fields in C#. However, if your organization has just purchased the software that contains the Employee class, obviously all of the employees will have the same company name. This means that it would be wasteful to store the company name separately for each employee. You'd just be duplicating the string unnecessarily. Instead, what you want is just to store the company name once, and then have every employee object access the same data. This is how a static field works. Declare a static field, companyName:

 class Employee { private string name; private decimal salary;  private static readonly string companyName; 

In this code, you have simply declared another field, but by marking it as static you have instructed the compiler to store this variable only once, no matter how many Employee objects are created. In a real sense, this static field is associated with the class as a whole, rather than with any one object.

You have also declared this field as read-only. This makes sense because, like the employee's name, the company name should not be changed when the program is running.

Of course, merely declaring this field isn't enough. You also need to make sure it is initialized with the correct data. Where should you do that? Not in your constructor; the constructor is called every time you create an Employee object, whereas you only want to initialize companyName once. The answer is that C# provides another construct for this purpose, known as the static constructor. The static constructor acts like any other constructor, but it works for the class as a whole, not for any particular object. If you define a static constructor for a class, it will be executed just once. As a rule, it will execute before any client code attempts to access the class, typically when the program first starts up. Add a static constructor to the Employee class:

 static Employee() {  companyName = "Wrox Press Pop Stars";  }  

As usual, you identify the constructor because it has the same name as the class. This one is also identified as static; hence, it is the static constructor. It is marked neither as public nor as private because it is called by the .NET runtime, not by any other C# code. So, just for the static constructor, you don't need any access modifier.

In the example, you have implemented the static constructor by hard-coding in a company name. More realistically, you might read a registry entry or a file, or connect to a database to find out the company name. Incidentally, because the companyName field has been declared as both static and read-only, the static constructor is the only place in which you can legally assign a value to it. You have one last thing to do, which is to define a public property that lets you access the company name.

 public static string CompanyName  {  get  {  return companyName; }  }  

The CompanyName property has also been declared as static, and you can now see the real significance of a static method or property: a method or property can be declared as static if it accesses only static fields and does not access any data that is associated with a particular object.

As you have already seen, the syntax for calling static members of the class from outside the class is slightly different from that used for other members. Because a static member is associated with the class rather than with any object, you use the class name rather than the name of a variable to call it:

 string Company = Employee.CompanyName;  

The concept of static members is very powerful and provides a very useful means for a class to implement any functionality that is the same for every object of that class. The only way that you can achieve anything like this in Visual Basic is by defining global variables. The disadvantage of global variables is that they are not associated with any particular class, and this can lead to name conflict issues.

Here are two more situations in which you might use static class members:

  • You might choose to implement a MaximumLength property for your Employee class, or for that matter for any other class that contains a name, where you might need to specify the maximum length of the name.

  • In C#, most of the numeric data types have static properties that indicate their maximum possible values. For example, to find out the biggest values that can be stored in an int and a float, you could write:

 int MaxIntValue = int.MaxValue; float MaxFloatValue = float.MaxValue;  

Inheritance

In this section you take a closer look at how implementation inheritance works. Suppose that a year after you have shipped your software package it's time for the next version. One point that your customers have commented on is that some of their employees are actually managers, and managers usually get profit-related bonuses as well as regular salaries. This means that your GetMonthlyPayment() method doesn't give the complete information for managers. The practical upshot of this is that you have to have some way of dealing with managers, too.

For the purposes of this example, assume that the bonus is some constant figure that can be specified when you create a manager. You don't want to get bogged down in doing profit-related calculations here.

If you were coding in Visual Basic, how would you set about upgrading your software? There are two possible approaches; both of them have severe disadvantages:

  • You could write a new class, Manager.

  • You could modify the Employee class.

Writing a new class is probably the approach that would result in the least amount of work for you, because you'd probably start by simply copying and pasting all the code for the Employee class module and then modifying your copy of the code. The trouble is that Employee and Manager have an awful lot of code in common, such as the code surrounding the Name, CompanyName, and Salary properties. Having the same code duplicated is dangerous. What happens if, at some point in the future, you need to modify the code? Some poor developer is going to have to remember to make exactly the same changes to both classes. That is just asking for bugs to creep in. Another problem is that there are now two unrelated classes that client code will have to deal with, which is likely to make it harder for the people writing the code that uses Employee and Manager. (Although you could get around this by wrapping the common properties into an interface and having both Employee and Manager implement this interface.)

A slightly different alternative is to write a Manager class and put an Employee object inside it as a class-scoped variable. This solves the problem of duplicating code, but still leaves you with two separate objects, as well as an awkward, indirect, syntax for calling employee methods and properties (for example, objManager.objEmployee.Name).

If you opt for modifying the Employee class module, then you could, for example, add an extra field, a Boolean, that indicates whether or not this Employee is a manager. Then, at relevant parts of the code, you would test this Boolean in an If statement, to check what to do. This solves the problem of having two unrelated classes. However, it introduces a new difficulty: As mentioned earlier, you decide a year or so later to add manager support. This means that the Employee class module has presumably been shipped, tested, fully debugged, and is known to be working correctly. Do you really want to have to dive in and start pulling working code to bits, with all the associated risk of introducing new bugs?

In short, you have reached a point at which Visual Basic cannot offer any satisfactory solutions. Enter C#, which does offer a way out of this quandary, through inheritance.

As mentioned earlier, inheritance involves adding or replacing features of classes. In the previous example, the SquareRootForm class added stuff to the .NET class, System.Windows.Forms.Form. It defined the controls to go on the SquareRootForm as member fields, and also added an event handler. The Employee example will demonstrate both adding and replacing features of a base class. You will define a Manager class, which is derived from Employee. You will add a field and property that represent the bonus, and replace the GetMonthlyPayment() method (for completeness, you'll also replace ToString() so that it displays the bonus as well as the name and salary). This all means that you will have a separate class. But you do not need to duplicate any code, nor do you need to make any big changes to the Employee class either. You might think that you still have a problem of two different classes — which makes it more difficult to write client code. However, C# provides a solution for this problem as well.

Inheriting from the Employee Class

Before you define the Manager class you need to make one small change to Employee — you have to declare the GetMonthlyPayment() method as virtual:

 public virtual decimal GetMonthlyPayment() { return salary/12; } 

Roughly speaking, this is the C# way of saying that this is a method that in principle can be overridden.

Note

You might think that this means you are changing the base class, which invalidates the argument about not needing to change the base class. However, adding a virtual keyword isn't really the sort of major change that carries a risk of new bugs — with the Visual Basic approach you were going to have to actually rewrite the implementations of several methods. Besides, usually when you write classes in C#, you plan in advance for the methods that are suitable candidates for overriding. If this was a real-life example, GetMonthlyPayment() would almost certainly have been declared virtual in the first place, so then you really would have been able to add the Manager class without making any changes to the Employee class.

The Manager Class

You can now define the Manager class:

 class Manager : Employee { private decimal bonus; public Manager(string name, decimal salary, decimal bonus): base(name, salary) { this.bonus = bonus; } public Manager(string name, decimal salary): this(name, salary, 100000M) { } public decimal Bonus { get { return bonus; } } public override string ToString() { return base.ToString() + ", bonus: " + bonus; } public override decimal GetMonthlyPayment() { return base.GetMonthlyPayment() + bonus/12; } } 

Besides the near-complete implementation of the Employee class that you have inherited, Manager contains the following members:

  • A field, bonus, which will be used to store the manager's bonus, and a corresponding property, Bonus

  • The overloaded GetMonthlyPayment() method, as well as a new overload of ToString()

  • Two constructors

The bonus field and corresponding Bonus property shouldn't need any further discussion. However, the next section looks in detail at the overridden methods and the new constructors, because they illustrate important C# language features.

Method Overrides

The override of GetMonthlyPayment() is reasonably simple. Notice that you have marked it with the keyword override to tell the compiler that you are overriding a base class method, as you did with Employee.ToString():

 public override decimal GetMonthlyPayment() { return base.GetMonthlyPayment() + bonus/12; } 

Your override also contains a call to the base-class version of this method. This method uses a new keyword, base. base works in the same way as your override, except that it indicates that you want to grab a method, or property, from the definition in the base class. Alternatively, you could have implemented your override of GetMonthlyPayment() like this:

 public override decimal GetMonthlyPayment() { return (Salary + bonus)/12; } 

However, you cannot use this code:

 public override decimal GetMonthlyPayment() { return (salary + bonus)/12; // wrong } 

This code looks almost exactly like the previous version, except that you are hitting the salary field directly instead of going through the Salary property. You might think that this looks like a more efficient solution, because you are saving what is effectively a method call. The trouble is that the compiler will raise an error because the salary field has been declared as private. That means that nothing outside the Employee class is allowed to see this field. Even derived classes are not aware of private fields in base classes.

If you do want derived classes to be able to see a field, but not unrelated classes, C# provides an alternative level of protection, protected:

 protected decimal salary; // we could have done this 

If a member of a class is declared as protected, it is visible only in that class and in derived classes. However, in general, you are strongly advised to keep all fields private for exactly the same reason that you are advised to keep variables private in Visual Basic class modules: by hiding the implementation of a class (or class module) you are making it easier to carry out future maintenance of that class. Usually, you will use the protected modifier for properties and methods that are intended purely to allow derived classes access to certain features of the base class definition.

The Manager Constructors

You need to add at least one constructor to the Manager class for two reasons:

  • There is now an extra piece of information, the manager's bonus, which you need to specify when you create a Manager instance.

  • Unlike methods, properties, and fields, constructors are not inherited by derived classes.

In fact, you have added two constructors. This is because you have decided to assume that the manager's bonus normally defaults to $100,000 if it is not explicitly specified. In Visual Basic you can specify default parameters to methods, but C# does not allow you to do this directly. Instead, C# offers a more powerful technique that can achieve the same effect, method overloads. Defining two constructors here will illustrate this technique.

The first Manager constructor takes three parameters:

 public Manager(string name, decimal salary, decimal bonus) : base(name, salary) {  this.bonus = bonus;  }  

The first thing you notice about this constructor is a call to the base class constructor using a slightly strange syntax. The syntax is known as a constructor initializer. What happens is that any constructor is allowed to call one other constructor before it executes. This call is made in a constructor initializer with the syntax shown in the preceding code. It is permitted for a constructor to call either another constructor in the same class, or a constructor in the base class. This might sound restrictive, but it is done for good reasons in terms of imposing a well-designed architecture on the constructors. These issues are discussed in Chapter 3, "Objects and Types." The syntax for the constructor initializer requires a colon, followed by one of the keywords base or this to specify the class from which you are calling the second constructor, followed by the parameters you are passing on to the second constructor.

The constructor shown in the preceding code takes three parameters. However, two of these parameters, name and salary, are really there in order to initialize base class fields in Employee. These parameters are the responsibility of the Employee class rather than the Manager class, so what you do is simply pass them on to the Employee constructor for it to deal with — that's what the call to base(name, salary) achieves. And as you saw earlier, the Employee constructor will simply use these parameters to initialize the name and salary fields. Finally, you take the bonus parameter, which is the responsibility of the Manager class, and use it to initialize the bonus field. The second Manager constructor that you've supplied also uses a constructor initialization list:

 public Manager(string name, decimal salary) : this(name, salary, 100000M) {  }  

In this case, what is happening is that you set up the value of the default parameter, and then pass everything on to the three-parameter constructor. The three-parameter constructor, in turn, calls the base class constructor to deal with the name and salary parameters. You might wonder why you haven't used the following alternative way of implementing the two-parameter constructor:

 public Manager(string name, decimal salary,) : base(name, salary) // not so good  {  this.bonus = 100000M;  }  

The reason is that this involves some potential duplication of code. The two constructors each separately initialize the bonus field, and this might cause problems in the future in terms of both constructors needing separately to be modified if, for example, in some future version of Manager you change how you store the bonus. In general, in C# just as in any programming language, you should avoid duplicating code if you can. For this reason, the previous implementation of the two-parameter constructor is preferred.

Method Overloading

The fact that you have supplied two constructors for the Manager class illustrates the principle of method overloading in C#. Method overloading occurs when a class has more than one method of the same name, but different numbers of parameters. In the case of method overloading, the same principles apply as in constructor overloading.

Note

Don't confuse the terms method overloading and method overriding. Despite the similar names, they are different, and completely unrelated, concepts!

When the compiler encounters a call to a method that has been overloaded, it examines the parameters you are attempting to pass in, in order to figure out which method is the one to call. In the case of creating a Manager object, because one constructor takes three parameters and the other only takes two, the compiler examines the number of parameters first. Hence, if you write

 Manager SomeManager = new Manager("Name", 300000.00M); 

the compiler will arrange for a Manager object to be instantiated, with the two-parameter constructor being used, which means that the bonus will be given its default value of 100000M. If, on the other hand, you write this:

 Manager SomeManager = new Manager ("Name", 300000.00M, 50000.00M); 

the compiler will arrange for the three-parameter constructor to be called, so bonus will now be given the specified value of 50000.00M. If several overloads are available, but the compiler is unable to find one that is suitable, it will raise a compilation error. For example, if you wrote

 Manager SomeManager = new Manager (100, 300000.00M, 50000.00M); // wrong 

you would get a compilation error because both of the available Manager constructors require a string, and not a numeric type, as the first parameter. The C# compiler can arrange for some type conversions between the different numeric types to be done automatically, but it will not convert automatically from a numeric value to a string.

Note that C# does not allow methods to take default parameters in the way Visual Basic does. However, it is very easy to achieve the same effect using method overloads, as done in this example. The usual way is simply to have the overloads with fewer parameters supply default values for the remaining parameters and then call the other overloads.

Using the Employee and Manager Classes

Now that you have completed defining the Employee and Manager classes, you can write some code that uses them. In fact, if you download the source code for this project from the Wrox Press Web site (www.wrox.com), you will find that these two classes are defined as part of a standard Windows Forms project, quite similar to the SquareRoot sample. In this case, however, the main form has only one control, a list box. You use the constructor of the main form class (MainForm) to instantiate a couple of instances of Employee and Manager objects and then display data for these objects in the list box. Figure B-3 shows the results of this operation.

image from book
Figure B-3

The code used to generate these results is this:

 public MainForm() { InitializeComponent(); Employee Britney = new Employee("Britney Spears", 20000.00M); Employee Elton = new Manager("Elton John", 50000.00M); Manager Ginger = new Manager("Geri Halliwell", 50000.00M, 20000.00M); this.listBox1.Items.Add("Elton's name  this.listBox1.Items.Add("Elton's salary this.listBox1.Items.Add("Elton's bonus  this.listBox1.Items.Add("Elton's monthly Elton.GetMonthlyPayment()); this.listBox1.Items.Add("Elton's company this.listBox1.Items.Add("Elton.ToString(): " + Elton.ToString()); this.listBox1.Items.Add("Britney.ToString(): " + Britney.ToString()); this.listBox1.Items.Add("Ginger.ToString(): " + Ginger.ToString()); } 

This code should be self-explanatory, based on the C# that you have learned up to now, apart from one little oddity — one of the Manager objects, Elton, is being referred to by an Employee reference instead of a Manager reference. How does this work? Keep reading.

References to Derived Classes

Take a closer look at the Manager class that is referenced by a variable declared as a reference to Employee:

 Employee Elton = new Manager("Elton John", 50000.00M); 

This is perfectly legal C# syntax. The rule is quite simple: if you declare a reference to a type B, then that reference is permitted to refer to instances of B or to instances of any class derived from B. This works because any class derived from B must also implement any methods or properties and soon that B implements. So in the previous example, you call Elton.Name, Elton.Salary, and Elton.GetMonthlyPayment().The fact that Employee implements all these members guarantees that any class derived from Employee will do the same. So it doesn't matter if a reference points to a derived class — you can still use the reference to call up any member of the class the reference is defined as and be confident that that method exists in the derived class.

On the other hand, notice the syntax that is used when you call the Bonus property against Elton: ((Manager)Elton).Bonus. In this case, you need to convert Elton to a Manager reference, because Bonus is not implemented by Employee. The compiler knows this and would raise a compilation error if you tried to call Bonus through an Employee reference. That line of code is shorthand for writing:

 Manager ManagerElton = (Manager) Elton; this.listBox1.Items.Add("Elton's bonus is " + ManagerElton.Bonus);  

As in Visual Basic, conversion between data types in C# is known as casting. You can see from the previous code that the syntax for casting involves placing the name of the destination data type in parentheses before the name of the variable you are attempting to cast. Of course, the object being referred to must be of the correct type in the first place. If you wrote

 Manager ManagerBritney = (Manager) Britney;  

the code would compile correctly, but when you ran it, you would get an error, because the .NET runtime would see that Britney is just an Employee instance, not a Manager instance. References are permitted to refer to instances of derived classes, but not to instances of base classes of their native type. It's not permitted for a Manager reference to refer to an Employee object. (You can't permit it because if you did, what would happen if you attempted to call the Bonus property through such a reference?)

Because Visual Basic doesn't support implementation inheritance, there is no direct parallel in Visual Basic for C#'s support for references referring to objects of derived classes. However, there is some similarity with the fact that in Visual Basic you can declare an interface reference, and then it does not matter what type of object that interface refers to, as long as the object in question implements that interface. If you were coding the Employee and Manager classes in Visual Basic, you might as well have done so by defining an IEmployee interface that both class modules implement, and then access the Employer features through this interface.




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

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