We said earlier that arrays, as simple collections, contain items that are all of the same type: all int (egers), for example, or all (references to) Student objects. As it turns out, this is true of collections in general: we'll typically want to constrain them to contain similarly typed objects. However, the power of inheritance steps in to make collections quite versatile.
It turns out that if we declare an array to hold objects of a given type—e.g., Person —then we're free to insert objects explicitly declared to be of type Person or of any types derived from Person —for example, UndergraduateStudent, GraduateStudent, and Professor. This is due to the "is a" nature of inheritance: UndergraduateStudent, GraduateStudent, and Professor objects, as subclasses of Person, are simply special cases of Person objects. The C# compiler would therefore be perfectly happy to see code as follows:
Person people = new Person; // of Person object references Professor p = new Professor(); UndergraduateStudent s1 = new UndergraduateStudent(); GraduateStudent s2 = new GraduateStudent(); // Add a mixture of professors and students in random order to the array; // as long as Professor, UndergraduateStudent, and GraduateStudent are // all derived from Person, the compiler will be happy! people = s1; people = p; people = s2; // etc.
As we'll see when we discuss C# collection types in more detail in Chapter 13, C# collections other than Arrays actually don't allow us to specify what type of object they will hold when we declare a collection, as illustrated here:
ArrayList list = new ArrayList(); // No type designated! // ArrayLists hold generic Objects.
Student s = new Student; // With Arrays, we DO specify a // type (Student, in this case).
Most C# collections are automatically designed to hold objects of type Object, which as we learned in an earlier chapter is the superclass of all other classes in the C# language, user defined or otherwise. So, from the compiler's perspective we can put whatever types of object we wish into a collection in any combination. But, it's still important that you, as the programmer, know what the intended base type for a collection is going to be from an application design standpoint, so that you discipline yourself to only insert objects of the proper type (including derived types of that type) into the collection. This will be important when we subsequently attempt to iterate through and process all of the objects in the collection: we'll need to know what general class of object they are, so that we'll know what methods they can be called upon to perform.We'll talk about this in more detail when we discuss polymorphism in Chapter 7.