带着问题读CLR via C#(四)基元类型,引用类型和值类型(下)

Q1: 一个值类型调用System.Object类定义的方法会不会发生装箱?

A1: 如果值类型重写了System.Object定义的虚方法(Equals, GetHashCode, ToString),调用时不会发生装箱,如果重写的方法中调用了基类的实现,则需要进行装箱;如果值类型调用了非虚方法(GetType, MemberwiseClone),则会发生装箱。

 

Q2: 为什么重写Equals方法需要同时重写GetHashCode方法?

A2: GetHashCode方法可以获取任意对象的Int32哈希码,System.Collections.HashTable类型,System.Generic.Dictionary类型以及其他一些集合的实现中,要求两个对象为了相等,必须拥有相同的哈希码。

 

Q3: System.Object类定义的Equals方法是用来判断对象的相等性还是同一性?

A3: System.Object类定义的Equals方法如下:

1 public virtual Boolean Equals(Object obj)
2 {
3     if (this == obj)
4         return true;
5     return false;
6 }

它其实判断的是对象的同一性而不是相等性,通过如下代码可验证:

 1 namespace Test
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {            
 7             Person p1 = new Person() { Name = "Allen", Sex = true, Age = 25 };
 8             Person p2 = new Person() { Name = "Allen", Sex = true, Age = 25 };
 9 
10             Console.WriteLine(p1.Equals(p2));        
11         }
12     }
13 
14     class Person
15     {
16         public string Name { get; set; }
17         public bool Sex { get; set; }
18         public int Age { get; set; }
19     }
20 }

返回结果为false.

 

Q4: 如何重写Equals方法使它用来判断相等性而不是同一性?

A4: 可在上例中Person类中重写Equals方法:

 1 public override bool Equals(object obj)
 2 {
 3     if (obj == null)
 4         return false;
 5 
 6     if (obj.GetType() != this.GetType())
 7         return false;
 8 
 9     Person p = (Person)obj;
10     return this.Name == p.Name && this.Sex == p.Sex && this.Age == p.Age;
11 }

此时再运行上例中代码则会得到true.

PS: 除了重写Equals(object)外,还建议为自己的类型写Equals(Person)以增强性能:

1 public bool Equals(Person p)
2 {
3     if (p == null)
4         return false;
5     return Name == p.Name && Sex == p.Sex && Age == p.Age;
6 }

 

Q5: 重写了Equals方法后,如何判断两个对象的同一性?

A5: System.Object类提供了一个叫做ReferenceEquals的静态方法,可以用来比较同一性。

 

Q6: System.ValueType已经重写了System.Object的Equals方法,在自定义值类型的时候,还需要重写Equals方法吗?

A6: 最好重写,ValueType的Equals方法使用了反射技术,而CLR的反射机制很慢,重写Equals方法可提高性能。

 

Q7: 重写Equals方法应注意什么?

A7: 1)Equals必须是自反的,x.Equals(x)肯定为true; 2)Equals必须是对称的,x.Equals(y)返回的值一定等于y.Equals(x); 3)Equals必须是可传递的,x.Equals(y)返回true, y.Equals(z)返回true, 则x.Equals(z)也一定返回true; 4)Equals必须是一致的,比较的两个值没有变,Equals返回的值也不能变。

 

Q8: dynamic和var的区别是什么?

A8: dynamic是在运行时检测实际类型,var是在编译时编译器已经能够确定实际类型。例如定义了一个var str = "abc"; 这完全等同于定义了string str = "abc"; 编译器可以判断出str只能是string类型,用str调用string类型的方法和属性时,智能提示可以显示所有string类型的方法和属性,但当定义dynamic str = "abc"; 时,编译器并不知道str的实际类型,只会在运行时做判断,即使我们用str调用一个根本不存在的方法,同样可以通过变异,但运行时会报错。

posted @ 2013-04-04 15:36  Allen Li  阅读(1322)  评论(2编辑  收藏  举报