Chapter 3 - Object-Oriented C# | |
bySimon Robinsonet al. | |
Wrox Press 2002 | |
We indicated earlier that all .NET classes are ultimately derived from System.Object , and that, in C#, if you don't specify that a class is derived from another class, the compiler will automatically assume that it derives from 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.
The methods defined in Object are:
Method | Access Modifiers | Purpose |
---|---|---|
string ToString() | public virtual | Returns a string representation of the object |
int GetHashTable() | public virtual | Used if implementing dictionaries (hash tables) |
bool Equals(object obj) | public virtual | Compares instances of the object for equality |
bool Equals(object objA, object objB) | public static | Compares instances of the object for equality |
bool ReferenceEquals(object objA, object objB) | public static | Compares whether two references refer to the same object |
Type GetType() | public | Returns details of the type of the object. |
object MemberwiseClone() | protected | Makes a shallow copy of the object |
void Finalize() | protected virtual | This is the .NET version of a destructor |
Recall that C# uses the keyword object as a syntactical convenient shorthand for System.Object . Writing object instead of Object might not seem that much more convenient, but at least it means Visual Studio .NET will recognize it as a keyword and so display it in a different color .
At this stage, you should consider this table as mostly for reference. We haven't yet covered enough of the C# language to be able to properly understand how to use all these methods. We're not going to examine most of them or how to use them in detail yet for the most part, we'll do that in Chapter 5 when we look at the .NET base classes more thoroughly. We'll make a couple of observations however.
You'll gather from the three methods that are available to compare objects that .NET supports a fine degree of control in precisely how objects are compared. We'll go into this in detail in Chapter 5, the behavior of these may be overridden in some classes.
GetType() returns an instance of the class System.Type this is another of the many base classes that Microsoft has written for us, and which we'll examine in Chapter 5. Object.GetType() provides the usual means of access into the .NET reflection technology, allowing you to examine the definitions of types in assemblies.
We have already encountered ToString() in previous chapters. It forms the most convenient way to get a quick string representation of an object. We'll examine that method in detail now.
ToString() is an incredibly convenient way of getting a string representation of an object. For example:
int i = -50; string str = i.ToString(); // returns "-50"
Here's another example:
enum Colors {Red, Orange, Yellow}; // later on in code... Colors favoriteColor = Colors.Orange; string str = favoriteColor.ToString(); // returns "Orange"
Object.ToString() is actually declared as virtual, and in all these examples, we 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 our Colors enum counts as a predefined data type. It actually gets implemented as a struct derived from System.Enum , and System.Enum has 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, then your classes will simply inherit the System.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, then you will need to override it. We illustrate this with a sample, StringRepresentations , which defines two very simple classes that represent US$ money amounts. Money simply acts as a wrapper for the decimal class, while BetterMoney derives from Money and adds a ToString() override. The complete code for the sample is as follows . Note that it also illustrates use of properties to wrap fields, as well as inheritance:
using System; namespace Wrox.ProCSharp.OOCSharp { class MainEntryPoint { static void Main(string[] args) { Money cash1 = new Money(); cash1.Amount = 40M; Console.WriteLine("cash1.ToString() returns: " + cash1.ToString()); cash1 = new BetterMoney(); cash1.Amount = 40M; Console.WriteLine("cash1.ToString() returns: " + cash1.ToString()); Console.ReadLine(); } } class Money { private decimal amount; public decimal Amount { get { return amount; } set { amount = value; } } } class BetterMoney : Money { public override string ToString() { return "$" + Amount.ToString(); } } }
You'll realize that this sample is there just to illustrate syntactical features of C#. C# already has a predefined type to represent currency amounts, decimal , so in real life, you wouldn't write such a class to do this unless you wanted to add various other methods to it. And you'd probably use the String.Format() method (details in the MSDN docs, and we cover this method in Chapter 5 too) to format a currency string.
In the Main() method we instantiate first a Money object, then a BetterMoney() object. In both cases we call ToString() . For the Money object, we'll pick up the Object version of this method that displays class information. For the BetterMoney object, we'll pick up our own override. Running this code gives the following results:
StringRepresentations cash1.ToString() returns: Wrox.ProCSharp.OOCSharp.Money cash1.ToString() returns: