3.6 Object类
前面提到,所有的.NET类都派生于System.Object。实际上,如果在定义类时没有指定基类,编译器就会自动假定这个类派生于Object。本章没有使用继承,所以前面介绍的每个类都派生于System.Object(如前所述,对于结构,这个派生是间接的:结构总是派生于System.ValueType,System.ValueType派生于System.Object)。
其重要性在于,除了自己定义的方法和属性外,还可以访问为Object定义的许多公共或受保护的成员方法。这些方法可以用于自己定义的所有其他类中。
3.6.1 System.Object方法
在Object中定义的方法如表3-1所示。
|
方 法 |
访问修饰符 |
作 用 |
|
string ToString() |
public virtual |
返回对象的字符串表示 |
|
int GetHashTable() |
public virtual |
在实现字典(散列表)时使用 |
|
bool Equals(object obj) |
public virtual |
对对象的实例进行相等比较 |
|
bool Equals(object objA,object objB) |
public static |
对对象的实例进行相等比较 |
|
bool ReferenceEquals(object objA, object objB) |
public static |
比较两个引用是否指向同一个对象 |
|
Type GetType() |
public |
返回对象类型的详细信息 |
|
object MemberwiseClone() |
protected |
进行对象的浅表复制 |
|
void Finalize() |
protected virtual |
该方法是析构函数的.NET版本 |
我们还没有完整地介绍C#语言,所以用户还不能理解使用这些方法的方式。下面将简要总结每个方法的作用,但ToString()方法要详细论述。
● ToString()方法:是获取对象的字符串表示的一种便捷方式。当只需要快速获取对象的内容,以用于调试时就可以使用这个方法。在数据的格式化方面,它提供的选择非常少:例如,日期在原则上可以表示为许多不同的格式,但DateTime.ToString()没有在这方面提供任何选择。如果需要更专业的字符串表示,例如考虑用户的格式化配置或文化(区域),就应实现IFormattable接口(详见第8章)。
● GetHashTable()方法:如果对象放在名为映射(也称为散列表或字典)的数据结构中,就可以使用这个方法。处理这些结构的类使用该方法确定把对象放在结构的什么地方。如果希望把类用作字典的一个键,就需要重写GetHashTable()方法。对该方法重载的执行方式有一些相当严格的限制,这些将在第9章介绍字典时讨论。
● Equals()(两个版本)和ReferenceEquals()方法:如果把3个用于比较对象相等性的不同方法组合起来,就说明.NET Framework在比较相等性方面有相当复杂的模式。这3个方法和比较运算符==在使用方式上有微妙的区别。而且,在重写带一个参数的虚拟Equals()方法时也有一些限制,因为System.Collections命名空间中的一些基类要调用该方法,并希望它以特定的方式执行。第5章在介绍运算符时将探讨这些方法的使用。
● Finalize()方法:第7章将介绍这个方法,它最接近C++风格的析构函数,在引用对象被回收,以清理资源时调用。Finalize()方法的Object执行代码实际上什么也没有做,因而被垃圾收集器忽略。如果对象拥有对未托管资源的引用,则在该对象被删除时,就需要删除这些引用,此时一般要重写Finalize()。垃圾收集器不能直接重写该方法,因为它只负责托管的资源,只能依赖用户提供的Finalize()。
● GetType()方法:这个方法返回从System.Type派生的类的一个实例。这个对象可以提供对象所属类的更多信息,包括基本类型、方法、属性等。System.Type还提供了.NET反射技术的入口。这个主题详见第11章。
● MemberwiseClone()方法:这是System.Object中惟一没有在本书的其他地方详细论述的方法。不需要讨论这个方法,因为它在概念上相当简单,只是复制对象,返回一个对副本的引用(对于值类型,就是一个装箱的引用)。注意,得到的副本是一个简单复制,即它复制了类中的所有值类型。如果类包含内嵌的引用,就只复制引用,而不复制引用的对象。这个方法是受保护的,所以不能用于复制外部的对象。该方法不是虚拟的,所以不能重写它的实现代码。
3.6.2 ToString()方法
第2章已经提到了ToString()方法,它是快速获取对象的字符串表示的最便捷的方式。
例如:
int i = –50;
string str = i.ToString(); // returns "–50"
下面是另一个例子:
enum Colors {Red, Orange, Yellow};
// later on in code...
Colors favoriteColor = Colors.Orange;
string str = favoriteColor.ToString(); // returns "Orange"
Object.ToString()声明为虚类型,在这些例子中,该方法的实现代码都是为C#预定义数据类型重写过的代码,以返回这些类型的正确字符串表示。Colors枚举是一个预定义的数据类型,它实际上实现为一个派生于System.Enum的结构,而System.Enum有一个相当聪明的ToString()重写方法,来处理用户定义的所有枚举。
如果不在自己定义的类中重写ToString(),该类将只继承System.Object执行方式—— 显示类的名称。如果希望ToString()返回一个字符串,其中包含类中对象的值信息,就需要重写它。下面用一个例子Money来说明这一点。在该例子中,定义一个非常简单的类Money,表示美元数。Money是decimal类的包装器,提供了一个ToString()方法。注意,这个方法必须声明为override,因为它将替代(重写)Object提供的ToString()方法。第4章将详细讨论重写。该例子的完整代码如下所示(注意它还说明了如何使用属性封装字段):
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());
Console.ReadLine();
}
}
class Money
{
private decimal amount;
public decimal Amount
{
get
{
return amount;
}
set
{
amount = value;
}
}
public override string ToString()
{
return "$" + Amount.ToString();
}
}
}
这个例子仅说明了C#的语法特性。C#已经有表示货币的预定义类型decimal。所以在现实生活中,不必编写这样的类来重复该功能,除非要给它添加其他方法。在许多情况下,由于格式化要求,也可以使用String.Format()方法(详见第8章)格式化货币字符串,而不是ToString()。
在Main()方法中,先实例化一个Money对象,再实例化BetterMonny对象。在这两个实例化过程中都调用了ToString()。对于Money对象,选择了该方法的Object版本,显示类的信息。对于BetterMonny对象,选择了我们自己的重写方法。运行这段代码,会得到如下结果:
StringRepresentations
cash1.ToString() returns: $40






