1. 值类型和引用类型的区别?html
2. 结构和类的区别?c++
3. delegate是引用类型仍是值类型?enum、int[]和string呢?面试
4. 堆和栈的区别?数组
5. 什么状况下会在堆(栈)上分配数据?它们有性能上的区别吗?函数
6.“结构”对象可能分配在堆上吗?什么状况下会发生,有什么须要注意的吗?性能
7. 理解参数按值传递?以及按引用传递?学习
8. out
和 ref
的区别与相同点?测试
9. C#支持哪几个预约义的值类型?C#支持哪些预约义的引用类型?ui
10. 有几种方法能够断定值类型和引用类型?spa
11. 说说值类型和引用类型的生命周期?
12. 若是结构体中定义引用类型,对象在内存中是如何存储的?例以下面结构体中的class类 User对象是存储在栈上,仍是堆上?
public struct MyStruct { public int Index; public User User;
万变不离其宗,只要搞清楚值类型和引用类型的原理,上面全部题目就都迎刃而解了。
CLR支持两只类型:引用类型和值类型。这是.NET语言的基础和关键,他们从类型定义、实例建立、参数传递,到内存分配都有所不一样。虽然看上去简单,但真正理解其内涵的人却好像并很少。
下图清晰了展现了.NET中类型分类,值类型主要是一些简单的、基础的数据类型,引用类型主要用于更丰富的、复杂的、复合的数据类型。
值类型和引用类型最根源的区别就是其内存分配的差别,在这以前首先要理解CLR的内存中两个重要的概念:
Stack 栈:线程栈,由操做系统管理,存放值类型、引用类型变量(就是引用对象在托管堆上的地址)。栈是基于线程的,也就是说一个线程会包含一个线程栈,线程栈中的值类型在对象做用域结束后会被清理,效率很高。
GC Heap托管堆:进程初始化后在进程地址空间上划分的内存空间,存储.NET运行过程当中的对象,全部的引用类型都分配在托管堆上,托管堆上分配的对象是由GC来管理和释放的。托管堆是基于进程的,固然托管堆内部还有其余更为复杂的结构,有兴趣的能够深刻了解。
结合下图理解,变量a及其值3都是存储在栈上面。变量b在栈上存储,其值指向字符串“123”的托管堆对象地址(字符串是引用类型,字符串对象是存储在托管堆上面。字符串是一个特殊的引用类型,后面文章会专门探讨)”
值类型一直都存储在栈上面吗?全部的引用类型都存储在托管堆上面吗?
1.单独的值类型变量,如局部值类型变量都是存储在栈上面的;
2.当值类型是自定义class的一个字段、属性时,它随引用类型存储在托管堆上,此时她是引用类型的一部分;
4.全部的引用类型确定都是存放在托管堆上的。
5.还有一种状况,同上面题目12,结构体(值类型)中定义引用类型字段,结构体是存储在栈上,其引用变量字段只存储内存地址,指向堆中的引用实例。将值类型的变量赋值给另外一个变量(或者做为参数传递),会执行一次值复制。将引用类型的变量赋值给另外一个引用类型的变量,它复制的值是引用对象的内存地址,所以赋值后就会多个变量指向同一个引用对象实例。理解这一点很是重要,下面代码测试验证一下:
int v1 = 0; int v2 = v1; v2 = 100; Console.WriteLine("v1=" + v1); //输出:v1=0 Console.WriteLine("v2=" + v2); //输出:v2=100 User u1=new User(); u1.Age = 0; User u2 = u1; u2.Age = 100; Console.WriteLine("u1.Age=" + u1.Age); //输出:u1.Age=100 Console.WriteLine("u2.Age=" + u2.Age); //输出:u2.Age=100,由于u1/u2指向同一个对象
当把对象做为参数传递的时候,效果同上面同样,他们都称为按值传递,但由于值类型和引用类型的区别,致使其产生的效果也不一样。
private void DoTest(int a) { a *= 2; } private void DoUserTest(User user) { user.Age *= 2; } [NUnit.Framework.Test] public void DoParaTest() { int a = 10; DoTest(a); Console.WriteLine("a=" + a); //输出:a=10 User user = new User(); user.Age = 10; DoUserTest(user); Console.WriteLine("user.Age=" + user.Age); //输出:user.Age=20 }
上面的代码示例,两个方法的参数,都是按值传递
按引用传递的两个主要关键字:out
和 ref
无论值类型仍是引用类型,按引用传递的效果是同样的,都不传递值副本,而是引用的引用(相似c++的指针的指针)。out
和 ref
告诉编译器方法传递额是参数地址,而不是参数自己,理解这一点很重要。
代码简单测试一下,若是换成out效果是相同的
private void DoTest( ref int a) { a *= 2; } private void DoUserTest(ref User user) { user.Age *= 2; } [NUnit.Framework.Test] public void DoParaTest() { int a = 10; DoTest(ref a); Console.WriteLine("a=" + a); //输出:a=20 ,a的值改变了 User user = new User(); user.Age = 10; DoUserTest(ref user); Console.WriteLine("user.Age=" + user.Age); //输出:user.Age=20 }
out
和 ref
的主要异同:
out
和 ref
都指示编译器传递参数地址,在行为上是相同的;out
和 ref
不能够重载,就是不能定义Method(ref int a)和Method(out int a)这样的重载,从编译角度看,两者的实质是相同的,只是使用时有区别;值类型包括简单类型、结构体类型和枚举类型,引用类型包括自定义类、数组、接口、委托等。
结构体是值类型,类是引用类型,主要区别如题1。其余的区别:
enum枚举是值类型,其余都是引用类型。
线程堆栈:简称栈 Stack
托管堆: 简称堆 Heap
结构是值类型,有两种状况会分配在对上面:
out
和 ref
的区别与相同点?out
和 ref
都指示编译器传递参数地址,在行为上是相同的;out
和 ref
不能够重载,就是不能定义Method(ref int a)和Method(out int a)这样的重载,从编译角度看,两者的实质是相同的,只是使用时有区别;值类型:整数、浮点数、字符、bool和decimal
引用类型:Object,String
简单来讲,继承自System.ValueType的是值类型,反之是引用类型。
值类型在做用域结束后释放。
引用类型由GC垃圾回收期回收。这个答案可能太简单了,更详细的答案在后面的文章会说到。
public struct MyStruct { public int Index; public User User; }
MyStruct存储在栈中,其字段User的实例存储在堆中,MyStruct.User字段存储指向User对象的内存地址。
版权全部,文章来源:http://www.cnblogs.com/anding
我的能力有限,本文内容仅供学习、探讨,欢迎指正、交流。
.NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引
Value type and Reference type Explained:http://www.tuicool.com/articles/MbUfA3U
C# 值类型与引用类型 (上):http://www.cnblogs.com/siqing99/archive/2012/04/03/2430918.html
书籍:CLR via C#
书籍:你必须知道的.NET