zoukankan      html  css  js  c++  java
  • 值类型不是值类型(ValueType is NOT a Value Type):闲谈.Net类型

     .Net的类型系统比较复杂,很多人经常给绕进来,比如《[原创]慢话interface是值类型还是引用类型》一文。而网上的、书上的关于.Net类型的表述一般是错误的或者不完全的,准确性最高的是MSDN上的表述,但那个表述又太简单了,让人很难理解。本文试着通俗的解释几个关于.Net类型的基础问题。

    1. 托管与非托管

     像现实生活中,有体制内和体制外之分,.Net 里也有托管和非托管之分。托管堆、GC这些是托管部分,非托管堆是非托管部分。
     托管部分叫做组织,体制内,非托管部分叫做群众,体制外。

    2. 引用类型是体制内的类型
     有组织关系的,体制内的类型叫做引用类型。体制内的类型(引用类型)有什么特点呢?它不能脱离组织。由组织 new 它,死后魂归组织。生命周期由组织来维护。生是组织的人,死是组织的鬼。它是受组织保护的,你不能用指针这样粗暴野蛮危险的东西来威胁体制内的类型。
     
    3. 值类型是体制外的类型

     体制外的类型是值类型(非托管堆 或者 栈中)。
     值类型要想进入体制内(托管堆),必须找到挂靠者。挂靠者必须是体制内的类型。这像贷款要担保一样,办杂志要找到托管单位一样,进入体制类,必须找到一个管理者。所以,一个值类型要想进入托管堆里,它必须是一个引用类型组成部分或者组成部分的组成部分……,它的生死存亡,和这个引用类型绑定在一起。

     装箱是一种特殊的挂靠。
     体制外的类型不受体制保护,所以可以用指针这样粗暴野蛮危险的东西来处置。
     
    4.值类型和引用类型的区别
     引用类型和值类型的区别是有没有组织关系,是体制内的还是体制外的,而不是常说的什么栈啊堆啊的blablabla。
     引用类型是体制内的,一生都在体制内呆着。而值类型是体制外的人,平时在栈里和非托管堆里生存,想进体制内得挂靠体制内的人。
     由于身份的不同,所受的待遇也不同。这就好比,一个群众和一个官员去办事,所受到的待遇不同一个道理。
     

    5.办事大厅——栈

     办事(函数调用)在栈里进行。

    6.办事待遇

     体制内的和体制外的办事待遇(传参)不一样。

     组织里的人办事只用打电话就行了,不用亲自去。当然,他也没办法亲自去,他的脑袋、胳膊、腿都是组织的,都在托管堆上,托管堆不会放他走,他根本去不了。而体制外的人办事得亲自去。当然也可以不亲自去,但是得特殊处理(ref,out,或者指针)。

    7.为什么要分体制内和体制外
     像java都是体制内的该多好啊。.net 说不,要进行体制改革,让大量的个头小的类型变成体制外的,不然的话体制负担就太重了。
     
    8. 接口是什么
     接口是一个逻辑上的概念,是对类型行为的规范和约束。比如欧盟不承认中国的市场经济地位,他们觉得中国没有实现“市场经济地位”的一些规范。
     
    9. 接口的实现
     逻辑上的东西只落在头脑中和纸面上,而在现实中,逻辑上的东西是需要实现来保证的接口有很多种实现方式。比如,C++中根本没提供接口的具体实现,那么经常将抽象类当接口用。再比如,haXe的typedef 实际上也是接口的一种实现,它用于编译时类型检测。
     
    10. .Net 中接口的实现方案
     接口有很多种实现方案,.Net选择了一种自己认为合理的一种:用引用类型来实现接口。
     当然,.Net完全可以把接口实现为值类型,或者值类型与引用类型之外的第三种类型。而.Net既然把它实现为引用类型,我们就得接受这个观点,受它的约束。

    11. 根据10,接口是引用类型,是体制内的类型

    12. 从值类型转换为它所实现的接口类型要装箱

    public struct Person : IPerson
    {
        public Int32 Age;
        public void SetAge(int age)
        {
            Age = age;
        }
     
        public Int32 GetAge()
        {
            return Age;
        }
    }
     
    public interface IPerson
    {
        void SetAge(int age);
        Int32 GetAge();
    }
     
    Person p = new Person();
    p.SetAge(5);
    Console.WriteLine(p.Age);
    IPerson ip = p;
    ip.SetAge(8);
    Console.WriteLine(ip.GetAge());
    Console.WriteLine(p.GetAge());

    上面的代码输出结果是:

    5
    8
    5

    13. 在特殊情况下,接口是性能杀手

     操作100万个值类型和将100万个值类型转换为引用类型再进行操作,性能大不一样。这时切忌为了抽象为了代码的优美,而用接口来操作值类型,性能会大幅度下降,GC也会表示压力很大。我就犯过这个错误,将图像的像素实现某个接口,用接口来操作像素。

    14. 白条现象

     不要认为某值类型实现了接口,它就“是”接口。实际上,它装箱后才是接口。这就好比打白条,白条可以转换为现金,但白条和现款是两码事。
     而从12的测试结果看,值类型和接口类型是两码事。
     一个值类型“实现”接口,意思是,这个值类型可以转换为该接口类型。

    15. 绕口令:值类型不是值类型

     typeof(ValueType).IsValueType == false 

     System.ValueType 是引用类型。
     
    16. 值类型隐式继承自System.ValueType 类型的意思是值类型可以转换为System.ValueType 类型,转换过程中有装箱现象。这也是一种“白条”现象。

     在 12 的代码基础上进行下列测试:

                Person p = new Person();
                p.SetAge(5);          

                Console.WriteLine(p.Age);
                IPerson ip = p;
                ip.SetAge(8);
                ValueType vt = p;
                Person vtp = (Person)vt;
                vtp.SetAge(9);
                Console.WriteLine(ip.GetAge());
                Console.WriteLine(p.GetAge());           

                Console.WriteLine(vtp.GetAge());

    结果是:

    5
    8
    5
    9

    17. 所有类型隐式继承自Object类型是他们可以转换为Object 类型,当然,值类型转换过程中也有装箱现象。这也是一种“白条”现象。

                Person p = new Person();
                p.SetAge(5);
                Console.WriteLine(p.Age);
                IPerson ip = p;
                ip.SetAge(8);
                ValueType vt = p;
                Object obj = p;
                Person vtp = (Person)vt;
                Person objp = (Person)p;
                vtp.SetAge(9);
                objp.SetAge(12);
                Console.WriteLine(ip.GetAge());
                Console.WriteLine(p.GetAge());
                Console.WriteLine(vtp.GetAge());
                Console.WriteLine(objp.GetAge());

    结果是:

    5
    8
    5
    9
    12

    18. END

  • 相关阅读:
    samba服务器常用指令
    如何将根文件系统制作成yaffs格式,并设置从yaffs启动
    如何从NFS文件系统启动
    zImage转换为uImage
    转载 uboot 命令
    xml的使用入门
    oracle(2)oracle的基础入门
    redis(3)redis的基础入门(java)
    redis(2)redis的基础入门(linux)
    redis(1)redis的安装
  • 原文地址:https://www.cnblogs.com/xiaotie/p/2676450.html
Copyright © 2011-2022 走看看