Reference and value types support different memory models. References refer to objects created on the managed heap, whereas value types are created on the stack. Equivalence and identity complement the memory model. Equivalence is the value or state of an instance.
Equivalent values are related types that contain the same value. In the following code, integers
locala
and
localc
are equivalent. However, the variables are not identical because they are stored at different locations on the stack. The
int locala=5; int localb=10; int localc=5;
Assigning a value type copies the value. The target and source are equivalent after this assignment, but not identical:
locala=localb; // locala and localb are equivalent.
For reference types, there is synchronicity of equivalence and identity. Related references containing the same value are equivalent and identical. Assigning a reference creates an alias, which can create unplanned side affects. For this reason, be careful when assigning references. The following code has some
using System; namespace Donis.CSharpBook{ public class XInt { public int iField=0; } public class Starter{ public static void Main(){ XInt obj1=new XInt(); obj1.iField=5; XInt obj2=new XInt(); obj2.iField=10; // Alias created and second instance lost obj2=obj1; obj1.iField=15; // side affect Console.WriteLine("{0}", obj2.iField); // 15 } } }
Classes can be refined through inheritance, which is code reuse. The common
Inheritance and polymorphism and other strategies to refine class usage are reviewed in Chapter 3.
Classes are often
Inheritance involves a
base type
and a
derived type
, where a derived type is a kind of base type. Derived types inherit the
A base type represents a generalization, whereas a derived type represents a specialty. Specialties are derivatives of
Derived types refine base types. A base type holds the common members of the generalization. Derived classes inherit those members and add specialty members. These members refine the base class in the context of the derived class. The
Employee
class, which is a base class, defines the
FullName
and
EmplID
members. Every employee has a
Inheritance provides hierarchical clarity. The employee and derived types form a hierarchy. Without this clarity, you face a greater challenge in programming an application. Classes are analogous to marbles in a bin. A large bin holds hundreds of marbles. The bin is an indiscriminate jumble of marbles. From this
Figure 3-1:
A
Figure 3-2:
Organized
As part of the design phase, scenarios are often employed. The nouns in the scenarios are candidates for classes. Figure 3-3 shows the nouns found in a scenario for a Personnel application. There is no order or hierarchy. You should look for relationships. The "is a" or "is a kind of" phrases are
Figure 3-3:
Nouns from the Personnel scenario
Figure 3-4:
Nouns grouped by relationships
Class hierarchy in
Inheritance also promotes code reuse. Without inheritance, common members would be implemented repeatedly—once in each derived type. Modifications would require updating several classes. This is a recipe for problems and is
Reference types can inherit classes and interfaces. Inheriting a class exemplifies code reuse. The derived class inherits the members—including the code—of the base class. Conversely, an interface has no code. It is a contract. Types that implement the interface contract to implement every member of the interface, whereas the interface contracts to be immutable. This is important because changes to the interface would break any type derived from that interface. Value types and primitives are not inheritable; they are sealed. For example, you cannot inherit an int.
Value types cannot inherit other value types or classes. The exception is the System.ValueType class. Value types implicitly inherit the System.ValueType class. A struct can inherit interfaces.
Terminology is important. Accurate terminology enhances a conversation, whereas incorrect or unclear terminology muddles the discussion. Object-oriented analysis and design introduced a
|
Inheritable Class |
Inheriting Class |
|---|---|
|
Superclass |
Subclass |
|
Parent |
Child |
|
Ascendant |
Descendant |
|
Base |
Derived |
Inheritance is not a mechanism to simply drop members into a class. From a design perspective, those members should belong in that class. For example, a timestamp has the properties of time and a stamp. A timestamp is a kind of stamp and has a time piece. However, a timestamp is not a kind of time. A TimeStamp class should inherit from the Stamp class but not the Time class. The "has a" relationship between the timestamp and time infers containment. Embedding a Time class in the TimeStamp class is the preferable implementation.