8.1 万物归宗:System.Object
本节将介绍以下内容:
System.Object类型解析
Object类型的常用方法及其应用
8.1.1 引言
正如标题所示,System.Object是所有类型的基类,任何类型都直接或间接继承自System.Object类。没有指定基类的类型都默认继承于System.Object,从而具有Object的基本特性,这些特性主要包括:
l 通过GetType方法,获取对象类型信息。
l 通过Equals、ReferenceEquals和==,实现对象判等。
l 通过ToString方法,获取对象字符串信息 ,默认返回对象类型全名。
l 通过MemberwiseClone方法,实现对象实例的浅拷贝。
l 通过GetHashCode方法,获取对象的值的散列码。
l 通过Finalize方法,在垃圾回收时进行资源清理。
接下来,我们和这些公共特性一一过招,来了解其作用和意义,深入其功能和应用。
8.1.2 初识
有了对Object类型的初步认识,我们使用Reflector工具加载mscorlib程序集来反编译Sytem.Object的实现情况,首先不关注具体的实现细节,将注意力放在基本的类型定义上:
public class Object
{
//构造函数
public Object() { }
public virtual int GetHashCode() { }
//获取对象类型信息
public System.Type GetType() { }
//虚方法,返回对象的字符串表示方式
public virtual string ToString() { }
//几种对象判等方法
public virtual bool Equals(object obj) { }
public static bool Equals(object objA, object objB) { }
public static bool ReferenceEquals(object objA, object objB) { }
//执行对象的浅拷贝
protected object MemberwiseClone() { }
//析构函数
protected virtual void Finalize() { }
}
从反编译代码中可知,System.Object主要包括了4个公用方法和2个受保护方法,其具体的应用和实现在后文表述。
8.1.3 分解
下面,我们选择Object的几个主要的方法来分析其实现,以便从整体上把握对Object的认知。
1.ToString解析
ToString是一个虚方法,用于返回对象的字符串表示,在Object类型的实现可以表示为:
public virtual string ToString()
{
return this.GetType().FullName.ToString();
}
可见,默认情况下,对象调用ToString方法将返回类型全名称,也就是命名空间加类型名全称。在通常的情况下,ToString方法提供了在子类中重新覆写基类方法而获取对象当前值的字符串信息的合理途径。例如,下面的类型MyLocation将通过ToString方法来获取其坐标信息:
class MyLocation
{
private int x = 0;
private int y = 0;
public override string ToString()
{
return String.Format("The location is ({0}, {1}).", x, y);
}
}
而.NET框架中的很多类型也实现了对ToString方法的覆写,例如Boolean类型通过覆写ToString来返回真或者假特征:
public override string ToString()
{
if (!this)
{
return "False";
}
return "True";
}
ToString方法,可以在调试期快速获取对象信息,但是Object类型中实现的ToString方法还是具有一些局限性,例如在格式化、语言文化方面Object.ToString方法就没有更多的选择。解决的办法就是实现IFormattable接口,其定义为:
public interface IFormattable
{
string ToString(string format, System.IFormatProvider formatProvider);
}
其中,参数format表明要格式化的方式,而参数formatProvider则提供了特定语言文化的信息。事实上,.NET基本类型都实现了IFormattable接口,以实现更灵活的字符串信息选择。以DateTime类型的ToString方法为例,其实现细节可表示为:
public struct DateTime : IFormattable
{
public string ToString(string format, IFormatProvider provider)
{
return DateTimeFormat.Format(this, format,
DateTimeFormatInfo.GetInstance(provider));
}
}
我们可以通过控制format参数和provider参数来实现特定的字符串信息返回,例如要想获取当前线程的区域性长格式日期时,可以以下面的方式实现:
DateTime dt = DateTime.Now;
string time = dt.ToString("D", DateTimeFormatInfo.CurrentInfo);
而想要获取固定区域性短格式日期时,则以另外的设定来实现:
DateTime dt = DateTime.Now;
string time = dt.ToString("d", DateTimeFormatInfo.InvariantInfo);
关于ToString方法,还应指出的是System.String类型中并没有实现IFormattable接口,System.String.ToString方法用来返回当前对象的一个引用,也就是this。
2.GetType解析
GetType方法为非虚的,用于在运行时通过查询对象元数据来获取对象的运行时类型。因为子类无法通过覆写GetType而篡改类型信息,从而保证类型安全。例如在下面的示例中:
class MyType
{
}
class Test_GetType
{
public static void Main()
{
MyType mt = new MyType();
//使用Object.GetType返回Type实例
Type tp = mt.GetType();
//返回类型全名称
Console.WriteLine(tp.ToString());
//仅返回类型名
Console.WriteLine(tp.Name.ToString());
}
}
//执行结果
//InsideDotNet.Framework.Object.MyType
//MyType
GetType返回的是一个System.Type或其派生类的实例。而该实例对象可以通过反射获取类型的元数据信息。从可以提供所属类型的很多信息,例如字段、属性和方法等,例如:
class MyType
{
private int number = 0;
private string name = null;
public static void ShowType(string type, string info)
{
Console.WriteLine("This type is MyType.");
}
private void ShowNumber()
{
Console.WriteLine(number.ToString());
}
}
class Test_GetType
{
public static void Main()
{
MyType mt = new MyType();
//根据Type实例查找类型成员
foreach (MemberInfo info in tp.GetMembers())
{
Console.WriteLine("The member is {0}, {1}", info.Name, info.DeclaringType);
}
//根据Type实例查找类型方法
foreach (MethodInfo mi in tp.GetMethods())
{
Console.WriteLine("The method is {0}", mi.ToString());
//查找方法参数信息
ParameterInfo[] pis = mi.GetParameters();
foreach (ParameterInfo pi in pis)
{
Console.WriteLine("{0}'s member is {1}", mi.ToString(), pi.ToString());
}
}
}
}
通过反射机制,就可以根据GetType方法返回的Type对象在运行期枚举出元数据表中定义的所有类型的信息,并根据System.Reflection空间中的方法获取类型的信息,包括:字段、属性、方法、参数、事件等,例如上例中就是根据System.Reflection中定义的相关方法来完成获取对象信息的处理过程。在晚期绑定的应用场合中,这种处理尤为常见。
.NET中,用于在运行期获取类型Type实例的方法并非只有Object.GetType方法,Type.GetType静态方法和typeof运算符也能完成同样的操作,不过在应用上有些区别,主要是:
l Type.GetType是非强类型方法;而typeof运算符支持强类型。
Type tp = Type.GetType("InsideDotNet.Framework.Object.MyType");
Type tp = typeof(InsideDotNet.Framework.Object.MyType);
l Type.GetType支持运行时跨程序集反射,以解决动态引用;而typeof只能支持静态引用。
Assembly ass = Assembly.LoadFrom(@"C:\Anytao.Utility.exe");
Type tpd = ass.GetType("Anytao.Utility.Message.AnyMsg");
Console.WriteLine(tpd.ToString());
注意:Type.GetType必须使用完全限定名,以避免模块依赖或循环引用问题。
另外,对于在运行期获取Type实例的方法,还可参考以下几种常见的方式,主要包括:
l 利用System.Reflection.Assembly的非静态方法GetType或GetTypes。
l 利用System.Reflection.Module的非静态方法GetType或GetTypes。
通过Assembly或Module实例来获取Type实例,也是程序设计中常见的技巧之一。
3.其他
l Equals静态方法、虚方法和ReferenceEquals方法用于对象判等,详细的应用请参考8.2节“规则而定:对象判等”。
l GetHashCode方法,用于在类型中提供哈希值,以应用于哈希算法或哈希表,不过值得注意的是对Equals方法和GetHashCode方法的覆写要保持统一,因为两个对象的值相等,其哈希码也应该相等,否则仅覆写Equals而不改变GetHashCode,会导致编译器抛出警告信息。
l Memberwise方法,用于在对象克隆时实现对象的浅拷贝,详细应用请参考7.7节“有深有浅的克隆:浅拷贝和深拷贝”。
l Finalize方法,用于在垃圾回收时实现资源清理,详细应用请参考5.3节“垃圾回收”。
8.1.4 意义
l 实现自上而下的单根继承。
l System.Object是一切类型的最终基类,也就意味着.NET的任何变量都是System.Object的实例,这种机制提供了不同类型之间进行交互通信的可能。也赋予了所有.NET基本类型的最小化功能方法,例如ToString方法、GetHashCode方法和Equals方法等。
8.1.5 结论
通过本节的论述,我们基本了解了System.Object类型的设计思路和实现细节,从框架设计的角度来看,我们应该了解和学习System.Object在设计与实现上的可取之道,一方面.NET框架提供了最小功能特征在子类中继承,另一方面则分别将不同的特征方法实现为不同的访问级别和虚方法,这些思路和技巧正是值得我们借鉴和深思的精华所在。






