在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1. C#的值类型和引用类型
C#的对象里面有两种类型,一个是引用类型,一个是值类型,值类型和引用类型的具体分类可以看下面的分类。
在C#中,不管是引用类型还是值类型,他们都隐式继承Object类(这里应该这样仔细理解,引用类型是直接地继承Object类,但是值类型是继承的是Object类的子类ValueType。
需要注意的是,C#的对象内存管理方式和C++或者Java都是有非常的不同的,简单来说,C#的内存组织方式是通过栈堆和托管堆的方式组织的。也就是下面这个图展示的那样:
2. Object.Equals方法:
Equal方法,顾名思义,就是一个辨别两个对象是否是一个对象的方法。
在C#中,对于引用类型,其实就是比较两个地址是否是相等的(也就是Object.ReferenceEquals是一样的)。
MSDN中对于怎么写引用类型的Eqaul方法给出了下面的建议:
关于值类型的Equal方法,MSDN给出了下面的建议:
MSDN文档告诉我们当我们无论是写一个值类型还是引用类型的集合,如果要写Equals方法,都要遵循下列规则:
3. 实现例子:
1. 实现引用类型的Equals:
对于引用类型来说,本身是很简单的,因为Object.ReferenceEquals就是一个判断两个对象是否是同一个引用的最直接的方法。但是需要注意的是,如果使用Object.ReferenceEquals判断两个值类型时,将永远返回false。
但是对于字符串常量来说有个很特别的地方就是,我们知道字符串在常量都是放在程序的常量区(data段),所以如果写出以下代码:
string str1 = "Fuck"; string str2 = "Fuck"; Object.ReferenceEquals(str1,str2)将会返回真。
2. 实现值类型的Equals:
我们如果想希望我们的Class实现值类型,那么有两个办法,第一个办法比较简单粗暴,直接重写Equal和GetHashCode,一定要记住要重写GetHashCode,比如我们可以写出下面的代码。
class Test1 { public string Name { get; set; } public Test1(string name) { Name = name; } public override bool Equals(object obj) { if (obj == null || obj.GetType() != typeof(Test1)) return false; Test1 testObj = obj as Test1; return this.Name == testObj.Name; } public static bool operator==(Test1 t1, Test1 t2) { return t1.Equals(t2); } public static bool operator !=(Test1 t1, Test1 t2) { return !(t1 == t2); } public override int GetHashCode() { return base.GetHashCode(); } } 这个时候我们可以把Test1来进行值的比较了,但是上面的代码并没有真正重载GetHashCode,以至于我们会导致下面的问题,我们写入下面的代码:
class Program { static void Main(string[] args) { Test1 t1 = new Test1("HaHa"); Test1 t2 = new Test1("HaHa"); Console.WriteLine("t1.Equals(t2)? {0}", t1.Equals(t2)); Console.WriteLine("t1 == t2? {0}", t1 == t2); List<Test1> list = new List<Test1>(); list.Add(t1); Console.WriteLine("list contains Test1(HaHa)? {0}", list.Contains(t2)); Console.ReadKey(); } } 这个时候看上去还很正常,但是如果我们加一点测试:
class Program { static void Main(string[] args) { Test1 t1 = new Test1("HaHa"); Test1 t2 = new Test1("HaHa"); Console.WriteLine("t1.Equals(t2)? {0}", t1.Equals(t2)); Console.WriteLine("t1 == t2? {0}", t1 == t2); ICollection<Test1> list = new HashSet<Test1>(); list.Add(t1); Console.WriteLine("list contains Test1(HaHa)? {0}", list.Contains(t2)); Dictionary<Test1, int> dict = new Dictionary<Test1, int>(); dict.Add(t1, 2); Console.WriteLine("dict contains Test1(HaHa)? {0}", dict.Keys.Contains(t2)); Console.ReadKey(); } } 这就出现了很奇怪的现象了,我们已经假定了我们的Test1是一个值类型了,我们如果把这个东西作为HashSet的类型或者Dictionary的键值的时候,却发现了这样的诡异的情况,我们认为相等的值不再相等。
这种情况的原因就是因为我们没有真正重写GetHashCode,如果把GetHashCode的代码改成这样:
public override int GetHashCode() { return Name.GetHashCode(); } 这样改动以后我们的程序将会出现正确的结果:
当然了正如我们上面所说的,我们可以通过实现IEqualable<T>的泛型接口来实现强类型的比较:
class Test : IEquatable<Test> { public Test(string name) { Name = name; } public bool Equals(Test other) { return this.Name == other.Name; } public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return Name.GetHashCode(); } public string Name { get; set; } } 如果实现了IEqualable<T>的泛型接口,那么Equals就没必要重载了,这就是我们经常使用的StringBuilder的是实现方式(不过StringBuilder并没有实现IEqualable<T>这个接口,而是直接重载了Equals这个方法)。
如果把class改为struct,因为值类型不可能为null,所以可以这样写:
struct Test1 { public string Name { get; set; } public Test1(string name) { Name = name; } public override bool Equals(object obj) { if (obj is Test1) { return this.Name == ((Test1)obj).Name; } return false; } public static bool operator==(Test1 t1, Test1 t2) { return t1.Equals(t2); } public static bool operator!=(Test1 t1, Test1 t2) { return !(t1.Equals(t2)); } public override int GetHashCode() { return Name.GetHashCode(); } }
|
请发表评论