• .Net之美读书笔记1


    值类型和引用类型

    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返回false
        public 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);//True
        Console.WriteLine(result);
        result = rPoint1.Equals(rPoint2);//True
        Console.WriteLine(result);
    
        RefPoint rPoint3 = new RefPoint(2);
        RefPoint rPoint4 = new RefPoint(2);
    
        result = (rPoint3 == rPoint4);//False
        Console.WriteLine(result);
        result = rPoint3.Equals(rPoint4);//False
        Console.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
    

    分析复杂判等情况

    1. 装箱
    ValPoint vPoint1 = new ValPoint(1);
    result = object.ReferenceEquals(vPoint1, vPoint1);
    Console.WriteLine(result);//False
    

    小伙伴困惑了,为什么明明是同一个变量放回false。其实ReferenceEquals函数的参数类型为object类型,
    这里发生了隐式装箱,所以在堆(Heap)为两个不同实例。
    2. 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方法。
    3. 值类型中含有引用类型成员

    	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 之美》 作者:张子阳

  • 相关阅读:
    设计模式之代理模式
    Java8内存模型—永久代(PermGen)和元空间(Metaspace)
    使用jconsole分析内存情况-JVM
    JVM(Java虚拟机)优化大全和案例实战
    JVM调优总结 -Xms -Xmx -Xmn -Xss
    聊聊jvm的PermGen与Metaspace
    Jconsole
    【Java VisualVM】使用 VisualVM 进行性能分析及调优
    JAVA基础(10)——IO、NIO
    JAVA基础(9)——容器(3)——并发容器
  • 原文地址:https://www.cnblogs.com/LoveTomato/p/8006690.html
Copyright © 2020-2023  润新知