zoukankan      html  css  js  c++  java
  • .Net常见面试题整理(一)——值类型和引用类型

    原文地址:http://www.cnblogs.com/zhangkai2237/archive/2013/03/17/2964528.html

    类型一直是C#中最基本的问题,关于值类型和引用类型,我想每个C#程序员都知道“值类型保存在上,引用类型保存在上”。但是仅仅知道到这里是完全不够的,我们需要理解C#中的类型,了解为什么要有值类型和引用类型以及他们的特征。

    一、值类型和引用类型的概念
     
            值类型的实例是在线程栈上分配的(不能免俗的提起这句话),值类型的变量并没有一个指向实例的指针,而是变量中已经包含了实例本身的字段。
            相应的引用类型的实例时在托管堆中分配的,返回的是一个指向实例对象的内存地址。
     
            比如我们一个值类型的变量 valType, 他包含一个int的字段a,其值为5,他在栈和堆中的示意图为:
        现有一个引用类型的变量refType,他指向RefType类的一个实例,下图为示意图:
            另外我们都知道基元类型中除了string类型,其他的都是值类型,但是我们大部分人都没有发现他们之间的区别。只要我们进入各种基元类型的定义中就可以发现:string类型是一个class,而其他的值类型都是struct。翻阅资料发现了微软在定义值类型和引用类型的区别:
            引用类型包括类和接口,所有的以class和interface修饰的类型都是引用类型;而值类型包括结构和枚举,所有的结构和枚举都是值类型。继续查找资料发现所有的结构都是抽象类型System.ValueType,所有的枚举都是派生自System.Enum类型的,而System.Enum类型也继承自System.ValueType类。所以我们可以得出值类型都是继承自System.ValueType的结论。
     
            值类型还有一个重要的特征是因为结构是隐式密封的,所以我们没办法由自值类型来派生一个我们想要的类型来。例如我们无法从System.Int32(int)类派生出另外一个类型来。
     
    二、为什么要有值类型
            FCL中的绝大多数类型都是引用类型,那么为什么要有值类型呢?首先我们回顾下实例化一个引用类型的步骤:
                1. 计算好在托管堆上分配的内存大小,包括该类型实例的所有字段的大小,以及在托管堆中的两个“额外成员”(《CLR via C#》中的翻译):类型对象指针和同步块索引;
                2. 在托管堆中分配指定大小的内存块;
                3. 初始化对象的“类型对象指针”和“同步块索引”;
                4、调用类型的构造函数,如果调用的是有参构造函数,则还要向其传入参数。注意,如果要实例化子类对象,则必须先调用父类的构造函数,一直会追溯到System.Object。这部分具体的可以参看《C#入门经典》相关章节。
     
            如果所有的类型都是引用类型,那么我们程序的性能将显著下降。引用类型在性能方面还有一个重大的问题是垃圾回收。当没有变量指向某个对象时,那么该对象就会成为垃圾,就会成为GC回收的对象,而GC是相当耗费资源的。而使用值类型是不需要垃圾回收的,对象超过了其作用域就会自动销毁
     
            上面这段解释了为什么我们需要值类型,讲解了值类型在性能上的又是,那么我们为何还需要引用类型呢?
            
            我们都知道值类型的传值是要复制整个对象的,而引用类型仅仅是复制指向实例对象的指针。而且值类型不能被继承,所以值类型适合一些比较轻量和简单的类型,否则同样会出现性能问题。
            那么我们应该什么时候使用值类型呢:
    必须满足以下所有条件,否则不要定义成值类型
    第一,类型具有基元类型的行为。类型简单,其中没有成员会修改类型的任何实例字段。
    第二,类型不需要从其他任何类型继承。
    第三,类型不会派生出其他任何类型。
    除了满足以上全部条件,还必须满足以下条件中的一个。
    第一,类型的实例较小(约是16字节或者更小)。
    第二,类型实例较大,但不作为方法的实参传递,也不通过方法返回。(这样即使很大但是不需实参传递,不会进行复制的操作)
     
            到这里我们基本上把值类型和引用类型的一些基本知识了解完了,那么这部分面试官喜欢问什么问题呢?
    值类型和引用类型的区别
                    1. 值类型分配在内存栈上,引用类型分配在托管堆上。当一个值类型的变量赋给另一个值类型的变量时,会执行一次逐字段的复制,而一个引用类型的变量赋给另一个引用类型的变量时,仅仅会复制对象的内存地址
                    2. 基于上一条,多个引用类型的变量可以同时指向同一个对象,对其中的任何一个变量执行操作都会影响到另一个变量引用的对象。而每个值类型的变量都已经包含了自己的对象,所以对值类型对象的操作不会影响到另一个值类型变量
                    3. 值类型包括结构枚举,他们均间接或直接派生自System.ValueType类;引用类型包括类和接口,他们都派生自System.Object类(这一句是废话,所有的类型都派生自System.Object类,可说可不说)。
                    4. 值类型都是隐式密封的,不能将一个值类型作为基类来定义一个新的值类型或者引用类型,也因此值类型中不能包括虚方法(不能被继承,虚方法给谁重写呢)。
                    5、默认情况下,创建一个引用类型的变量时,他会被初始化为null;而创建一个值类型时,他的所有成员都会被初始化为0.
                    6. 值类型的变量一旦超过了其作用域,为他分配的内存就会被立即释放;而引用类型则会在托管堆里待一段时间,直到垃圾回收器将其回收。
                    7. 由于System.ValueType类重写了Equals方法,所以两个值类型的Equals方法会在两个对象的字段完全匹配的情况下返回true;而引用类型的Equals则会在两个变量引用同一个对象的情况下才返回true。(这一条不重要,不说也无所谓,但是如果被问到自己要有所了解)。
     
                数组是值类型还是引用类型?
                    我第一次遇到的这个问题的时候并没有特别注意,但是心想他既然问这样的问题,应该是引用类型,所以坚定的回答”引用类型“,让面试官看不出来我是猜的。所以童鞋们以后如果遇到类似的问题,即使是猜的也要理直气壮,否则即使你答对了,面试官也知道你是猜的,在这个问题上还是会被扣分的。
                    数组类型的确是引用类型,可能有部分童鞋不承认,那我们简单的写一个验证方法:
                
           #region 数组的类型
                int[] intArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                int[] copyArray = intArray;
                intArray[0] = 9;
                Console.WriteLine(copyArray[0]);
                #endregion

                    输出为:9.

     
                结构和类的区别
                    实际上就是值类型和引用类型的区别,对照着第一题回答就行了。
     
            到这里,值类型和引用类型还有一个特别重要的点没有提到,那就是装箱和拆箱。实际上是一个很简答的问题,但是如果你不了解,在面试时会很难回答。并且在日常的工作中很有可能经常掉入装箱的陷阱,对程序的性能有较大的影响。
  • 相关阅读:
    java.lang.NoSuchMethodError:antlr.collections.AST.getLine() I
    T7 java Web day01 标签HTML
    T6 s1 day19
    T5 s5 Day18
    T5 s4 Day 17
    T5 s3 day16
    T5 s2 Day 15
    T5 s1 day14
    T4 S03 day 12
    T4 S01 day1
  • 原文地址:https://www.cnblogs.com/fornet/p/2976150.html
Copyright © 2011-2022 走看看