分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

.Net之美读书笔记1

发布时间:2023-09-06 01:29责任编辑:沈小雨关键词:暂无标签

C#类型基础

值类型和引用类型

  1. 栈(stack)是一种先进后出的数据结构,在内存中,变量会被分配在栈上来进行操作。
  2. 堆(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);

    引用类型结构图:

装箱和拆箱

装箱的步骤

  1. 在堆(Heap)为新生成的对象实例分配内存
  2. 将栈(Stack)的值复制到堆(Head)生的对象
  3. 将堆创建的对象的地址返回给引用类型变量

     ???????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

    分析复杂判等情况

  4. 装箱

    ValPoint vPoint1 = new ValPoint(1);result = object.ReferenceEquals(vPoint1, vPoint1);Console.WriteLine(result);//False
    小伙伴困惑了,为什么明明是同一个变量放回false。其实ReferenceEquals函数的参数类型为object类型,
    这里发生了隐式装箱,所以在堆(Heap)为两个不同实例。
  5. override Equals方法

    ValPoint vPoint1 = new ValPoint(1);ValPoint vPoint2 = new ValPoint(1);object obj1 = vPoint1;object obj2 = vPoint2;result = (obj1.Equals(obj2));//True
    返回True,不知大家是否答对。因为struct隐式继承ValueType,所以obj1.Equals(obj2)实际调用的是ValueType类型override的Equals方法。
  6. 值类型中含有引用类型成员

    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

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved