[C#] 类型学习笔记三:自定义值类型
既前两篇之后,这一篇我们讨论通过struct 关键字自定义值类型。
在第一篇已经讨论过值类型的优势,节省空间,不会触发Gargage Collection等等。
在对性能要求比较高的场景下,通过struct代替类是不错的选择。
那么,比如我们定义一个Point 类型,里面包含两个左边X, Y。
public struct Point { public int X; public int Y; public Point(int x, int y) { X = x; Y = y; } }
是不是这样就OK了呢?
当然不是。因为我们必须尽量避免这个值类型被装箱。
一个良好的值类型的定义,必须充分考虑到这个值类型的使用场景,然后定义好所需要的成员函数,从而避免有的函数调用不到而将值类型装箱的情况。比如说,如果这个Point可能会被放到某个容器中,并且排序,那么Point就必须实现接口System.IComparable,实现CompareTo 方法和接口System.IComprable<T>中类型安全的CompareTo 方法。
老赵前辈的博文防止装箱落实到底,只做一半也是失败 给了一个非常好的例子,我把它引用过来做一点讨论。
博文中的场景是struct所定义的值类型MyKey需要用作字典的键。
我们以System.Collections.Hashtable为例,其构造函数为
public Hashtable( int capacity, IEqualityComparer equalityComparer )
这里面IEqualityComparer 是一个接口,用来作为HashTable的比较器。这个接口包含两个函数:Equals 和 GetHashCode 。
(在System.Collections.Generic.Dictionary,以及其他一些集合的视线中,要求两个对象为了相等,必须具有相同的哈希码——CLR via C# 第三版。所以在很多情况下,Equals 和 GetHashCode都是定义值类型必须重写的两个方法。)
当我们使用struct 定义的值类型来作为HashTable的key时,因为我们自定义的值类型中没有提供Equals 和 GetHashCode的实现,因此值类型被装箱,来调用ValueType的这两个函数。
那么如何实现Equals 和 GetHashCode这两个方法呢?
对于GetHashCode方法,我们使用
public override int GetHashCode() {}
来重写其内容,自定义的计算方式最好能够做到返回的hash值能均匀分布。
对于equals 呢?如果我们仅仅用如下方法是不够的
public override bool Equals(object that) {}
因为它提供的是和object类型的比较,我们真正需要的是和同样类型MyKey的比较。
那么是否再加上这个就够了?
public bool Equals(MyKey that) {}
确实差不多了,但是我们的MyKey需要指明是实现了哪一个接口。程序在运行时,这个接口中的Equals 方法因为在MyKey中被实现,所以才会直接调用MyKey中的 Equals方法。
原文中实现了IEquatable<MyKey>接口。
如果我们打开的Int32的定义看一看
namespace System {public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> { ........ public override int GetHashCode() { return this; } public override bool Equals(object obj) { return obj is int && this == (int)obj; } public bool Equals(int obj) { return this == obj; } .... } }
里面的Equals部分和 MyKey的定义一样,也是有两个实现。同时Int32 实现了IEquatable<int>接口。
相关阅读
------------------------------------------------
Felix原创,转载请注明出处,感谢博客园!
posted on 2014-03-30 08:04 Felix Fang 阅读(6035) 评论(0) 编辑 收藏 举报