As indicated earlier, all.NET classes are ultimately derived from System. Object. In fact, if you don’t specify a base class when you define a class, the compiler will automatically assume that it derives
from Object. Because inheritance has not been used in this chapter, every class you have seen here is actually derived from Sys tern. Obj ect. (As noted earlier, for structs this derivation is indirect: A struct is always derived from System. ValueType, which in turn derives from System. Object.)
The practical significance of this is that, besides the methods and properties and so on that you define, you also have access to a number of public and protected member methods that have been defined for the Object class. These methods are available in all other classes that you define.
System. Object Methods
For the time being, we simply summarize the purpose of each method in the following list, and then, in the next section, we provide more detail about the ToString () method in particular.
- ToString ( ) – This is intended as a fairly basic, quick-and-easjsstring representation; use it when you just want a quick idea of the contents of an object, perhaps for debugging purposes. It provides very little choice of how to format the data: Forexample, dates can in principle be expressed in a huge variety of different formats, but DateTime. ToString () does not offer you an~ choice in this regard. If you need a more sophisticated string representation that, for example, takes account of your formatting preferences or of the culture (the locale), then you should implement the IForrnattable interface .
- GetHashCode ( ) – This is used if objects are placed in a data structure known as a map (also known as a hash table or dictionary). It is used by classes that manipulate these structures in order to determine where to place an object in the structure. If you intend your class to be used as a key for a dictionary, you will need to override GetHashCode ( ) . Some fairlv strict requirements exist for how you implement your overload, and you learn about those when you exam in” dictionaries .
- Equals () (both versions) and ReferenceEquals ( ) – As you’ll gather by the existence of three different methods aimed at comparing the equality of objects, the .NET Framework has quite a sophisticated scheme for measuring equality. Subtle differences exist between how these three methods, along with the comparison operator, =>. are intended to be used. Not only that, but restrictions also exist on how you should override the virtual, one-parameter version of Equals () if you choose to do so, because certain base classes in the System. Co Ll ec t i on s namespace call the method and expect it to behave in certain ways. You explore the use of these .
- Finalize () – It is intended as the nearest that C# has’to C++-style destructors and is called when a reference object is garbage collected to clean up resources. The Object implementation of Finalize () actually does nothing and is ignored by the garbage collector. You will normally override Finalize () if an object owns references to unmanaged resources that need to be removed when the object is deleted. The garbage collector cannot do this directly because it only knows about managed resources, so it relies on any finalizers that you supply .
- GetType () – This method returns an instance of a class derived from System. Type. This object can provide an extensive range of information about the class of which your object is a member, including base type, methods, properties, and so on. System. Type also provides the entry point into .NET’s reflection technology. Chapter 13, “Reflection,” examines this topic. MemberwiseClone () – This is the only member of System. Object that isn’t examined in detail anywhere in the book. There is no need to because it is fairly simple in concept. It simply makes a copy of the object and returns a reference (or in the case of a value type, a boxed reference) to the copy. Note that the copy made is a shallow copy – this means that it copies all the value types in the class. If the class contains any embedded references, then only the references will be copied, not the objects referred to. This method is protected and so cannot be called to copy external objects. It is also not virtual, so you cannot override its implementation.
The ToString() Method
You’ve already encountered ToString () in Chapter 2, “C# Basics.” It provides the most convenient way to get a quick string representation of an object.
int i = -50;
string str = i.ToString (); II returns ‘-50’
Object. ToString () is actually declared as virtual, and all these examples are taking advantage of the fact that its implementation in the C# predefined data types has been overridden for us in order to return correct string representations of those types. You might not think that the Colors enum counts as a predefined data type. It actually gets implemented as a struct derived from System. Enwn, and Sys tern. Enumhas a rather clever override of ToString () that deals with all the enums you define.
If you don’t override ToString () in classes that you define, your classes will simply inherit the Systlilll.Object implementation – which displays the name of the class. If you want ToString () to return a string that contains information about the value of objects of your class, you will need to override it. To illustrate this, the following example, Money,defines a very simple class, also called Money,which represents U.S. currency amounts. Moneysimply acts as a wrapper for the decimal class but supplies a ToString () method. Note that this method must be declared as override because it is replacing (overriding) the ToString () method supplied by Object. Chapter 4 discusses overriding in more detail. The complete code for this example is as follows. Note that it also illustrates use of properties to wrap fields:
This example is here just to illustrate syntactical features of C#. C# already has a predefined type to represent currency amounts, deci~l, so in real life, you wouldn’t write a class to duplicate this functionality unless you wanted to add various other methods to it. And in many cases, due to ff>rmatting requirements, you’d probably use the String. Format () method (rather than ToString () to display a currency string.
In the Main () method, you first instantiate a Moneyobject. The ToString () method is then called, which actually executes the override version of the method. Running this code gives the following results
• cashl.ToString() returns: $40