C#类型基础
值类型和引用类型
- 栈(stack)是一种先进后出的数据结构,在内存中,变量会被分配在栈上来进行操作。
- 堆(heap)是用于为引用类型的实例(对象)分配空间的内存区域,在堆上创建一个对象,
会将对象的地址传给栈上的变量。
值类型
当声明一个值类型的变量的时候的时候,变量本身包含了值类型的全部字段,值变量分配到线程堆栈(Thread Stack)上。
???public struct ValPoint ???{ ???????public int x; ???????public ValPoint(int x) ???????{ ???????????this.x = x; ???????} ???} ???//调用 ???ValPoint vPoint1 = new ValPoint(10);
值类型结构图
- struct 调用struct上的方法前,需要对其所有的字段进行赋值;
- struct 有隐式的无参构造函数,构造函数对struct成员初始化(通过new 调用构造函数),值类型被赋予0,引用类型被赋予null;
struct 自定义构造函数时,构造函数内必须为每个struct成员初始化;
引用类型
当声明一个引用类型变量,并使用new操作符创建引用类型实例的时候,该引用变量会被分配到线程栈(Stack)上,变量保存了位于堆(Heap)上的引用类型的实例的内存地址。
public class RefPoint{ ???public int x; ???public RefPoint(int x) ???{ ???????this.x = x; ???}}//调用RefPoint rPoint1 = new RefPoint(10);
引用类型结构图:
装箱和拆箱
装箱的步骤
- 在堆(Heap)为新生成的对象实例分配内存
- 将栈(Stack)的值复制到堆(Head)生的对象
将堆创建的对象的地址返回给引用类型变量
???????int i = 1; ???????object boxed = i;
拆箱相反的操作,所以装箱和拆箱是耗时的操作。
对象判等
引用类型判等
System.Object基类型中判等方法有3个,对三个方法分析
//比较引用变量是否指向堆(Heap)上的同一实例对象public static bool ReferenceEquals(object objA, object objB){ ???return objA == objB;}//实例方法,供派生类重写(override),(派生类未重写默认用基类,但只有指向同一实例对象返回true)public virtual bool Equals(object obj){ ???return RuntimeHelpers.Equals(this, obj);}// 如果一个为null返回falsepublic static bool Equals(object objA, object objB){ ???return objA == objB || (objA != null && objB != null && objA.Equals(objB));}
下面通过一些代码来验证方法
RefPoint rPoint1 = new RefPoint(1);RefPoint rPoint2 = rPoint1;result = (rPoint1 == rPoint2);//TrueConsole.WriteLine(result);result = rPoint1.Equals(rPoint2);//TrueConsole.WriteLine(result);RefPoint rPoint3 = new RefPoint(2);RefPoint rPoint4 = new RefPoint(2);result = (rPoint3 == rPoint4);//FalseConsole.WriteLine(result);result = rPoint3.Equals(rPoint4);//FalseConsole.WriteLine(result);
值类型的判等
值类型隐式继承ValueType,ValueType继承Object并override了Equals方法。override后的Equals方法主要比较步骤
???public override bool Equals(object obj) ???{ ???????//1.传入的对象是否为null ???????if (obj == null) ???????{ ???????????return false; ???????} ???????//2.类型判等 ???????RuntimeType runtimeType = (RuntimeType)base.GetType(); ???????RuntimeType left = (RuntimeType)obj.GetType(); ???????if (left != runtimeType) ???????{ ???????????return false; ???????} ???????//3.为系统基本类型可直接比较 ???????if (ValueType.CanCompareBits(this)) ???????{ ???????????return ValueType.FastEqualsCheck(this, obj); ???????} ???????//4.利用反射遍历值类型成员(这里有递归) ???????FieldInfo[] fields = runtimeType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ???????for (int i = 0; i < fields.Length; i++) ???????{ ???????????object obj2 = ((RtFieldInfo)fields[i]).UnsafeGetValue(this); ???????????object obj3 = ((RtFieldInfo)fields[i]).UnsafeGetValue(obj); ???????????if (obj2 == null) ???????????{ ???????????????if (obj3 != null) ???????????????{ ???????????????????return false; ???????????????} ???????????} ???????????else if (!obj2.Equals(obj3)) ???????????{ ???????????????return false; ???????????} ???????} ???????return true; ???}
来电代码验证
ValPoint vPoint1 = new ValPoint(1);ValPoint vPoint2 = new ValPoint(1);result = vPoint2.Equals(vPoint2);Console.WriteLine(result);//True
分析复杂判等情况
装箱
小伙伴困惑了,为什么明明是同一个变量放回false。其实ReferenceEquals函数的参数类型为object类型,ValPoint vPoint1 = new ValPoint(1);result = object.ReferenceEquals(vPoint1, vPoint1);Console.WriteLine(result);//False
这里发生了隐式装箱,所以在堆(Heap)为两个不同实例。override Equals方法
返回True,不知大家是否答对。因为struct隐式继承ValueType,所以obj1.Equals(obj2)实际调用的是ValueType类型override的Equals方法。ValPoint vPoint1 = new ValPoint(1);ValPoint vPoint2 = new ValPoint(1);object obj1 = vPoint1;object obj2 = vPoint2;result = (obj1.Equals(obj2));//True
值类型中含有引用类型成员
public struct ValLine{ ???public RefPoint rPoint; ???public ValPoint vPoint; ???public ValLine(RefPoint rPoint,ValPoint vPoint) ???{ ???????this.rPoint = rPoint; ???????this.vPoint = vPoint; ???}}//调用 ???RefPoint rPoint = new RefPoint(1); ???ValPoint vPoint1 = new ValPoint(1); ???ValPoint vPoint2 = new ValPoint(1); ???ValLine vLine1 = new ValLine(rPoint, vPoint1); ???ValLine vLine2 = new ValLine(rPoint, vPoint2); ???result = vLine1.Equals(vLine2);//True
大家怎么想的放回true,ValueType写Equals。利用反射遍历ValLine 类型成员,RefPoint引用类型因为指向堆(Heap)上同一实例未返回false,继续遍历ValPoint 类型下的Equals的方法未返回false。
对象复制
浅复制
指的是对 对象的成员来区分的。对象的成员是值类型时会复制其本身;对象的成员是引用类型时仅仅复制引用,而不在堆(Heap)上创建新实例。
引用类型需要怎样实现浅复制,需要实现ICloneable接口。需要添加类public class RefLine:ICloneable{ ???public RefPoint rPoint; ???public ValPoint vPoint; ???public RefLine(RefPoint rPoint, ValPoint vPoint) ???{ ???????this.rPoint = rPoint; ???????this.vPoint = vPoint; ???} ???public object Clone() ???{ ???????return MemberwiseClone(); ???}}//调用验证值类型 ???ValPoint vPoint = new ValPoint(1); ???RefPoint rPoint = new RefPoint(2); ???ValLine vLine1 = new ValLine(rPoint, vPoint); ???ValLine vLine2 = vLine1; ???vLine2.vPoint.x = 3; ???vLine2.rPoint.x = 4; ???Console.WriteLine(vLine1.vPoint.x);//1 ???Console.WriteLine(vLine1.rPoint.x);//4//调用验证引用类型 ???ValPoint vPoint = new ValPoint(1); ???RefPoint rPoint = new RefPoint(2); ???RefLine rLine1 = new RefLine(rPoint, vPoint); ???RefLine rLine2 = rLine1.Clone() as RefLine; ???rLine2.vPoint.x = 3; ???rLine2.rPoint.x = 4; ???Console.WriteLine(rLine1.vPoint.x);//1 ???Console.WriteLine(rLine1.rPoint.x);//4
值类型浅复制结构图:
引用类型浅复制结构图:
深复制
深复制就是将引用成员指向的对象也进行复制。引用成员可能指向引用,所以深复制非常复杂,简单的解决办法序列化。
???[Serializable] ???public struct ValPoint ???{ ???????public int x; ???????public ValPoint(int x) ???????{ ???????????this.x = x; ???????} ???} ???[Serializable] ???public class RefPoint ???{ ???????public int x; ???????public RefPoint(int x) ???????{ ???????????this.x = x; ???????} ???} ???[Serializable] ???public class RefLine ???{ ???????public RefPoint rPoint; ???????public ValPoint vPoint; ???????public RefLine(RefPoint rPoint, ValPoint vPoint) ???????{ ???????????this.rPoint = rPoint; ???????????this.vPoint = vPoint; ???????} ???????public object Clone() ???????{ ???????????BinaryFormatter bf = new BinaryFormatter(); ???????????MemoryStream ms = new MemoryStream(); ???????????bf.Serialize(ms, this); ???????????//注意设置流的起始位置 ???????????ms.Position = 0; ???????????return (bf.Deserialize(ms)); ???????} ???} ???//调用 ???ValPoint vPoint = new ValPoint(1); ???RefPoint rPoint = new RefPoint(2); ???RefLine rLine1 = new RefLine(rPoint, vPoint); ???RefLine rLine2 = rLine1.Clone() as RefLine; ???????rLine2.vPoint.x = 3; ???rLine2.rPoint.x = 4; ???Console.WriteLine(rLine1.vPoint.x);//1 ???Console.WriteLine(rLine1.rPoint.x);//2
不可变类型
string类型是一种特殊的引用类型,称作不可变类型。那么我们需要怎样自己创建一个不可变类型?
不可变类型需要具有的两个特性
- 对象原子性:对象的状态是一个整体,如果一个字段改变,其他字段也要做出相应改变。
对象的常量性: 对象的状态一旦确定,就不能再次更改了。
public struct Address{ ???//对象的常量型 ???private readonly string province; ???private readonly string city; ???private readonly string zip; ???//对一般引用类型(常量性需要修改) ???private readonly string[] phones; ???//保证对象的原子性,构造函数每次要么成功要么失败 ???public Address(string province, string city, string zip, string[] phones) ???{ ???????this.city = city; ???????this.province = province; ???????this.zip = zip; ???????this.phones = phones; ???????CheckZip(zip); ???} ???public string Province { get { return province; } } ???public string City { get { return city; } } ???public string Zip { get { return zip; } } ???//防止修改引用类型内的成员变量 ???private string[] Phones ???{ ???????get ???????{ ???????????string[] rtn = new string[phones.Length]; ???????????phones.CopyTo(rtn, 0); ???????????return rtn; ???????} ???} ???private void CheckZip(string value) ???{ ???????string pattern = @"\d{6}"; ???????if (!Regex.IsMatch(value, pattern)) ???????{ ???????????throw new Exception("Zip is invalid!"); ???????} ???}}
《.Net 之美》 作者:张子阳
.Net之美读书笔记1
原文地址:http://www.cnblogs.com/LoveTomato/p/8006690.html