码迷,mamicode.com
首页 > Web开发 > 详细

.Net之美读书笔记1

时间:2017-12-08 19:33:05      阅读:232      评论:0      收藏:0      [点我收藏+]

标签:基础   copyto   else   reg   通过   lock   als   info   memory   

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返回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

    分析复杂判等情况

  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

标签:基础   copyto   else   reg   通过   lock   als   info   memory   

原文地址:http://www.cnblogs.com/LoveTomato/p/8006690.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!