博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 值类型 与引用类型
阅读量:7085 次
发布时间:2019-06-28

本文共 5192 字,大约阅读时间需要 17 分钟。

前言

一般来说,值类型存于栈,引用类型存在于堆,值类型转化为引用类型叫Box, 引用类型转为值类型在Unbox, 最近看了一本书发现值类型与引用类型的知识远不止这些。

我发现在一下几点我的理解一直是错误的:

错误1. struct是值类型,它与System.Object没有任何关系。

struct 直接基类是System.ValueType, 而它的基类就是Sysem.Object, 而且int, bool,等基本类型都是struct. 所以所有的C#类型都继承自System.Object.

错误2. 值类型转换为接口类型不会装箱

我们都知道object obj = 3 会装箱; 其实IEquatable<int> obj = 3; 和前面的一样也会装箱。

错误3. struct类型的equals 不会装箱。

不仅有装箱,而且还有两次!

错误4:装箱,拆箱的内存分布。

装箱后,会重新分配堆,除了相关的值,还有函数表指针等,内存对齐等,还有一个漏掉的是指向这个装箱后对象的引用。

 

正文

namespace ConsoleApplication1{    // System.ValueType -> System.Object    struct Point2D    {        public int X { get; set; }        public int Y { get; set; }        // version 1        public override bool Equals(object obj)        {            if (!(obj is Point2D)) return false;            Point2D other = (Point2D)obj;            return X == other.X && Y == other.Y;        }        // version 2        public bool Equals(Point2D other)        {            return X == other.X && Y == other.Y;        }        // version 3        public static bool operator ==(Point2D a, Point2D b)        {            return a.Equals(b);        }        public static bool operator !=(Point2D a, Point2D b)        {            return !(a == b);        }    }    struct Point2D_V4 : IEquatable
{ public int X { get; set; } public int Y { get; set; } // version 1 public override bool Equals(object obj) { if (!(obj is Point2D_V4)) return false; Point2D_V4 other = (Point2D_V4)obj; return X == other.X && Y == other.Y; } // version 2 public bool Equals(Point2D_V4 other) { return X == other.X && Y == other.Y; } // version 3 public static bool operator ==(Point2D_V4 a, Point2D_V4 b) { return a.Equals(b); } public static bool operator !=(Point2D_V4 a, Point2D_V4 b) { return !(a == b); } } public class Employee { public string Name { get; set; } // the hashcode is base on the its cotent public override int GetHashCode() { return Name.GetHashCode(); } } class Program { static void Main(string[] args) { // version 1 Point2D a = new Point2D(), b = new Point2D(); //object test = b; // box b from value type to reference type //a.Equals(b); // box a to invoke the virtual function Equals // if we have 10,000,000 points, we will do 20,000,000 box operation, on 32-bit system, one box will allocate 16 Bytes. // version 2 // How to avoid boxing override the equals. //a.Equals(b); // no need box a or b this time. // version 3 //bool isequal = a == b;// no boxing here. // it seems ok now, but there is a edge case will cause boxing at CLR generic. we need implement Iequatable //Point2D_V4 i = new Point2D_V4(), j = new Point2D_V4(); //IEquatable
k = i;// box occurs here, value type to interface requires boxing. //i.Equals(j);// no box occurs // once boxing, they are not have relationship to orignal type. so the best practice is let the value type immutable, like system.datetime. //Point2D_V4 point = new Point2D_V4 { X = 5, Y = 7 }; //Point2D_V4 anotherPoint = new Point2D_V4 { X = 6, Y = 7 }; //IEquatable
equatable = point; //boxing occurs here //equatable.Equals(anotherPoint); //returns false //point.X = 6; //point.Equals(anotherPoint); //returns true //equatable.Equals(anotherPoint); //returns false, the box was not modified! // then we need to talk about the hashcode, if you know about the hashkey in datastructure. the hashcode is used for identity one or more than one items. // here are some requirements to the hashcode function: //1. if two item is equal, the hashkey must be equal. //2. if two item is equal, the hashkey should not equal, but not must. //3. the GetHashCode function should fast. //4. the Hashcode should not change. HashSet
employees = new HashSet
(); Employee kate = new Employee { Name = "Kate Jones" }; employees.Add(kate); kate.Name = "Kate Jones-Smith"; // it really shock me, I don't notice this before. bool has = employees.Contains(kate); //returns false! } }}

  version 1:

  public override bool Equals(object obj)

     首先参数ojbect 会做一次装箱,然后调用Equals 由于是虚函数,而值类型没有函数指针表,无法调用虚函数,必须转为引用类型,才能调用,我用ILDasm的确可以看到做了Box.

Version 2: 当我们重载了Equals函数,可以避免调用虚函数,这样可以避免两次装箱。 version 3: 让比较更加完善,对== , != 进行重载。 version 4: 继承自IEquatable, 在CLR generics 中会用到。需要进一步的验证。 version 5: 对HashCode的设计做了一些建议。不要依赖于可变的东西,否则正如上面例子所演示的,潜在的不稳定性。 总结 1. 使用值类型,当我们会创建大量的实例,比如10,000,000 2. override Equals, oeverload Equals, implement IEquatable
, overload ==, !=, 3. override GetHashCode 4. 使值类型为不变 参考 <
>

 

转载于:https://www.cnblogs.com/ming11/p/4541108.html

你可能感兴趣的文章
elipse 插件初了解---扩展的加载过程
查看>>
JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版下载
查看>>
split-brain 脑裂问题(Keepalived)
查看>>
Mule ESB中entry-point-resolver的使用(2) Callable Entry Point Resolver
查看>>
mysql (master/slave)复制原理及配置
查看>>
关于ARM字节对齐的问题
查看>>
linux 3 步升级 wordpress
查看>>
PHP面试题集
查看>>
HashMap和Hashtable的区别
查看>>
g++编译过程和动态链接库
查看>>
centos下安装python3
查看>>
28份精美简历
查看>>
windows下整合nginx与php
查看>>
让消费者觉得占了便宜
查看>>
改变ListView快速滑块的图像
查看>>
MySQL主主复制,出错
查看>>
caffe中数据库的设计
查看>>
网络爬虫更新策略和分布式抓取系统机构
查看>>
clang记录
查看>>
java在线预览txt、word、ppt、execel,pdf代码
查看>>