既前两篇之后,这一篇我们讨论通过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>接口。

相关阅读

[C#] 类型学习笔记一:CLR中的类型,装箱和拆箱

[C#] 类型学习笔记二:详解对象之间的比较

[C#] 类型学习笔记三:自定义值类型的更多相关文章

  1. IOS 学习笔记 2015-03-20 OC-数值类型

    一 定义 在Objective-C中,我们可以使用c中的数字数据类型,int.float.long等.它们都是基本数据类型,而不是对象.也就是说,不能够向它们发送消息.然后,有些时候需要将这些值作为对 ...

  2. python3学习笔记三(数字类型,字符串)

    数字(Number)类型 有四种类型:整数.布尔型.浮点数和复数 int整数 bool布尔,如True float浮点数,1.23 complex复数,1+2j.1.2+2.3j 内置的 type() ...

  3. Java 学习笔记 (三) Java 日期类型

    以下内容摘自:  https://www.cnblogs.com/crazylqy/p/4172324.html import java.sql.Timestamp; import java.text ...

  4. Netty学习笔记(三) 自定义编码器

    编写一个网络应用程序需要实现某种编解码器,编解码器的作用就是讲原始字节数据与自定义的消息对象进行互转.网络中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码, ...

  5. [C#] 类型学习笔记二:详解对象之间的比较

    继上一篇对象类型后,这里我们一起探讨相等的判定. 相等判断有关的4个方法 CLR中,和相等有关系的方法有这么4种: (1) 最常见的 == 运算符 (2) Object的静态方法ReferenceEq ...

  6. [C#] 类型学习笔记一:CLR中的类型,装箱和拆箱

    在学习.NET的时候,因为一些疑问,让我打算把.NET的类型篇做一个总结.总结以三篇博文的形式呈现. 这篇博文,作为三篇博文的第一篇,主要探讨了.NET Framework中的基本类型,以及这些类型一 ...

  7. javascript学习笔记(四) Number 数字类型

    数字格式化方法toFixed().toExponential().toPrecision(),三个方法都四舍五入 toFixed() 方法指定小数位个数  toExponential() 方法 用科学 ...

  8. [读书笔记]C#学习笔记三: C#类型详解..

    前言 这次分享的主要内容有五个, 分别是值类型和引用类型, 装箱与拆箱,常量与变量,运算符重载,static字段和static构造函数. 后期的分享会针对于C#2.0 3.0 4.0 等新特性进行. ...

  9. Hadoop学习笔记—5.自定义类型处理手机上网日志

    转载自http://www.cnblogs.com/edisonchou/p/4288737.html Hadoop学习笔记—5.自定义类型处理手机上网日志 一.测试数据:手机上网日志 1.1 关于这 ...

随机推荐

  1. ElasticSearch 2.0以后的改动导致旧的资料和书籍需要订正的部分

    id原先是可以通过path指定字段的 "thread": { "_id" : { "path" : "thread_id" ...

  2. SpringBoot日志配置(详解) 涉及控制台输出日志、生成日志文件、日志级别修改、hibernate日志不输出

    写在前面 本篇主要讲述日志配置,看完本篇可以解决下述问题, 控制台输出日志.生成日志文件.日志级别修改.hibernate日志不输出 Git Demo Path:https://github.com/ ...

  3. python执行linux命令的两种方法

    python执行linux命令有两种方法: 在此以Linux常用的ls命令为例: 方法一:使用os模块 1 2 3 shell# python >> import os >> ...

  4. js 拼接字符串时,本来想要’#1′ ,返回的却是’#01′

    今天在操作一个元素时,id值是拼接的. var index = $(this).attr(‘index’);    //0var id = ‘#’ + (index+1);    //#01$(id) ...

  5. 创建、编译、执行 java程序

    java源文件(.java)——Java字节码文件(.class)——在java虚拟机上执行 其他语言很多是编译后执行,所以无法跨平台

  6. lintcode-6-合并排序数组

    合并排序数组 合并两个排序的整数数组A和B变成一个新的数组. 样例 给出A=[1,2,3,4],B=[2,4,5,6],返回 [1,2,2,3,4,4,5,6] 挑战 你能否优化你的算法,如果其中一个 ...

  7. 用glob()函数返回目录下的子文件以及子目录

    glob() 函数返回匹配指定模式的文件名或目录 相对于readdir()和opendir()来说,使用glob()函数会方便很多 代码1: <?php function getfilename ...

  8. Ubuntu编译内核树

    什么是内核树?刚开始我也没弄明白,通过这几天的学习,有所感悟,就说说我的理解吧!从形式上看,内核树与内核源码的目录结构形式是相同的,都是由各个层次的文件目录结构组成,但是其中的具体内容肯定是不同的.从 ...

  9. ismember matlab

    ismember 判断A中的元素在B中有没有出现 LIA = ismember(A,B) for arrays A and B returns an array of the same size as ...

  10. P1667 数列

    题目描述 给定一个长度是n的数列A,我们称一个数列是完美的,当且仅当对于其任意连续子序列的和都是正的.现在你有一个操作可以改变数列,选择一个区间[X,Y]满足Ax +Ax+1 +…+ AY<0, ...