常见面试题目:

1. 值类型和引用类型的区别?

2. 结构和类的区别?

3. delegate是引用类型还是值类型?enum、int[]和string呢?

4. 堆和栈的区别?

5. 什么情况下会在堆(栈)上分配数据?它们有性能上的区别吗?

6.“结构”对象可能分配在堆上吗?什么情况下会发生,有什么需要注意的吗?

7. 理解参数按值传递?以及按引用传递?

8. outref 的区别与相同点?

9. C#支持哪几个预定义的值类型?C#支持哪些预定义的引用类型?

10. 有几种方法可以判定值类型和引用类型?

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 = ;
int v2 = v1;
v2 = ;
Console.WriteLine("v1=" + v1); //输出:v1=0
Console.WriteLine("v2=" + v2); //输出:v2=100 User u1=new User();
u1.Age = ;
User u2 = u1;
u2.Age = ;
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 *= ;
} private void DoUserTest(User user)
{
user.Age *= ;
} [NUnit.Framework.Test]
public void DoParaTest()
{
int a = ;
DoTest(a);
Console.WriteLine("a=" + a); //输出:a=10
User user = new User();
user.Age = ;
DoUserTest(user);
Console.WriteLine("user.Age=" + user.Age); //输出:user.Age=20
}

上面的代码示例,两个方法的参数,都是按值传递

  • 对于值类型(int a) :传递的是变量a的值拷贝副本,因此原本的a值并没有改变。
  • 对于引用类型(User user) :传递的是变量user的引用地址(User对象实例的内存地址)拷贝副本,因此他们操作都是同一个User对象实例。

参数-按引用传递:

按引用传递的两个主要关键字:outref不管值类型还是引用类型,按引用传递的效果是一样的,都不传递值副本,而是引用的引用(类似c++的指针的指针)。outref告诉编译器方法传递额是参数地址,而不是参数本身,理解这一点很重要。

代码简单测试一下,如果换成out效果是相同的

private void DoTest( ref int a)
{
a *= ;
} private void DoUserTest(ref User user)
{
user.Age *= ;
} [NUnit.Framework.Test]
public void DoParaTest()
{
int a = ;
DoTest(ref a);
Console.WriteLine("a=" + a); //输出:a=20 ,a的值改变了
User user = new User();
user.Age = ;
DoUserTest(ref user);
Console.WriteLine("user.Age=" + user.Age); //输出:user.Age=20
}

outref的主要异同

  • outref都指示编译器传递参数地址,在行为上是相同的;
  • 他们的使用机制稍有不同,ref要求参数在使用之前要显式初始化,out要在方法内部初始化;
  • outref不可以重载,就是不能定义Method(ref int a)和Method(out int a)这样的重载,从编译角度看,二者的实质是相同的,只是使用时有区别;

常见问题

题目答案解析:

1. 值类型和引用类型的区别?

值类型包括简单类型、结构体类型和枚举类型,引用类型包括自定义类、数组、接口、委托等。

  • 1、赋值方式:将一个值类型变量赋给另一个值类型变量时,将复制包含的值。这与引用类型变量的赋值不同,引用类型变量的赋值只复制对象的引用(即内存地址,类似C++中的指针),而不复制对象本身。
  • 2、继承:值类型不可能派生出新的类型,所有的值类型均隐式派生自 System.ValueType。但与引用类型相同的是,结构也可以实现接口。
  • 3、null:与引用类型不同,值类型不可能包含 null 值。然而,可空类型允许将 null 赋给值类型(他其实只是一种语法形式,在clr底层做了特殊处理)。
  • 4、每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值,值类型初始会默认为0,引用类型默认为null。
  • 5、值类型存储在栈中,引用类型存储在托管堆中。

2. 结构和类的区别?

结构体是值类型,类是引用类型,主要区别如题1。其他的区别:

  • 结构不支持无惨构造函数,不支持析构函数,并且不能有protected修饰;
  • 结构常用于数据存储,类class多用于行为;
  • class需要用new关键字实例化对象,struct可以不适用new关键字;
  • class可以为抽象类,struct不支持抽象;

3. delegate是引用类型还是值类型?enum、int[]和string呢?

enum枚举是值类型,其他都是引用类型。

4. 堆和栈的区别?

线程堆栈:简称栈 Stack
托管堆: 简称堆 Heap
  • 值类型大多分配在栈上,引用类型都分配在堆上;
  • 栈由操作系统管理,栈上的变量在其作用域完成后就被释放,效率较高,但空间有限。堆受CLR的GC控制;
  • 栈是基于线程的,每个线程都有自己的线程栈,初始大小为1M。堆是基于进程的,一个进程分配一个堆,堆的大小由GC根据运行情况动态控制;

6.“结构”对象可能分配在堆上吗?什么情况下会发生,有什么需要注意的吗?

结构是值类型,有两种情况会分配在对上面:

  • 结构作为class的一个字段或属性,会随class一起分配在堆上面;
  • 装箱后会在堆中存储,尽量避免值类型的装箱,值类型的拆箱和装箱都有性能损失,下一篇会重点关注;

7. 理解参数按值传递?以及按引用传递?

  • 按值传递:对于值类型传递的它的值拷贝副本,而引用类型传递的是引用变量的内存地址,他们还是指向的同一个对象。
  • 按引用传递:通过关键字out和ref传递参数的内存地址,值类型和引用类型的效果是相同的。

8. outref的区别与相同点?

  • outref都指示编译器传递参数地址,在行为上是相同的;
  • 他们的使用机制稍有不同,ref要求参数在使用之前要显式初始化,out要在方法内部初始化;
  • outref不可以重载,就是不能定义Method(ref int a)和Method(out int a)这样的重载,从编译角度看,二者的实质是相同的,只是使用时有区别;

9. C#支持哪几个预定义的值类型?C#支持哪些预定义的引用类型?

值类型:整数、浮点数、字符、bool和decimal

引用类型:Object,String

10. 有几种方法可以判定值类型和引用类型?

简单来说,继承自System.ValueType的是值类型,反之是引用类型。

11. 说说值类型和引用类型的生命周期?

值类型在作用域结束后释放。

引用类型由GC垃圾回收期回收。这个答案可能太简单了,更详细的答案在后面的文章会说到。

12. 如果结构体中定义引用类型,对象在内存中是如何存储的?例如下面结构体中的class类 User对象是存储在栈上,还是堆上?

 public struct MyStruct
{
public int Index;
public User User;
}

MyStruct存储在栈中,其字段User的实例存储在堆中,MyStruct.User字段存储指向User对象的内存地址。

版权所有,文章来源:http://www.cnblogs.com/sagecheng/p/5960950.html

个人能力有限,本文内容仅供学习、探讨,欢迎指正、交流。

.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

 

.NET面试题01-值类型与引用类型的更多相关文章

  1. .NET面试题解析(01)-值类型与引用类型

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 常见面试题目: 1. 值类型和引用类型的区别? 2. 结构和类的区别? 3. delegate是引用类型还 ...

  2. .NET基础知识(01)-值类型与引用类型

    常见面试题目: 1. 值类型和引用类型的区别? 2. 结构和类的区别? 3. delegate是引用类型还是值类型?enum.int[]和string呢? 4. 堆和栈的区别? 5. 什么情况下会在堆 ...

  3. 一道面试题引发的思考(C#值类型和引用类型)

    某年某月,笔者去面试招行的一个外包项目,辗转来到面试地点以后,面试官给了我一份试卷,试卷只有两道题目,其中一道是这样的: 阅读以下程序 class Program { struct Point { p ...

  4. 枚举|标志枚举+|(或)和&(与)运算|类型转换|值类型和引用类型|传参|异常|垃圾回收

    枚举部分 enum 关键字用于声明枚举,即一种由一组称为枚举数列表的命名常量组成的独特类型. 通常情况下,最好是在命名空间内直接定义枚举,以便该命名空间中的所有类都能够同样方便地访问它. 但是,还可以 ...

  5. .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱

    .NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...

  6. C#之值类型和引用类型

    本文意在巩固基础知识,并不是对其进行深入剖析,还望理解.本文是对此项知识的整理文,有些内容来源于网络,其他为博主原创,所以难免会有一些小得瑕疵,敬请谅解.所有示例均是博主测试过的,如有转载请标明出处, ...

  7. .NET的堆和栈02,值类型和引用类型参数传递以及内存分配

    在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...

  8. c# 值类型与引用类型(转)

    本文意在巩固基础知识,并不是对其进行深入剖析,还望理解.本文是对此项知识的整理文,有些内容来源于网络,其他为博主原创,所以难免会有一些小得瑕疵,敬请谅解.所有示例均是博主测试过的,如有转载请标明出处, ...

  9. 20151024_001_C#基础知识(静态与非静态的区别,值类型和引用类型,堆和栈的区别,字符串的不可变性,命名空间)

    1:我们把这些具有相同属性和相同方法的对象进行进一步的封装,抽象出来类这个概念. 类就是个模子,确定了对象应该具有的属性和方法. 对象是根据类创建出来的. 2:类:语法 [public] class ...

  10. .NET中的值类型与引用类型

    .NET中的值类型与引用类型 这是一个常见面试题,值类型(Value Type)和引用类型(Reference Type)有什么区别?他们性能方面有什么区别? TL;DR(先看结论) 值类型 引用类型 ...

随机推荐

  1. .NET简单学习

    .NET是一个平台,对用户来说,只要下载了.NET Framework就可以运行.NET程序,获得需要的服务:对开发人员来说,平台对开发有着强有力的支持,方便创作各种应用软件. .NET Framew ...

  2. Qt Creator快捷键设置

    QT Creator 下载地址 http://download.qt.io/ 一.快捷键配置方法: 进入“工具->选项->环境->键盘”即可配置快捷键. 二.常用默认快捷键:   编 ...

  3. 利用Socket 实现多客户端的请求与响应

    import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Serve ...

  4. CENTOS7常用的基础命令集合(一)

    目录(?)[-] CentOS7 常用命令集合 常用命令 文件与目录操作 查看文件内容 文本内容处理 查询操作 压缩解压 yum安装器 网络相关 系统相关 系统服务启动相关 防火墙相关 RPM包管理 ...

  5. Redis-08.命令参数详解

    1. redis-cli -r(repeat)选项代表江命令执行多次 # 执行3次ping命令 redis-cli -r 3 ping -i(interval)选项代表每个几秒执行一次命令(必须和-r ...

  6. php安全编程&python测试实例编写

    前言 本文首发i春秋论坛. 本篇文章主要分享一个python暴破脚本,该脚本采用optparse模块,支持自定义字典位置:用了多线程(虽然我感觉和单线程速度差不多..是我的错觉还是线程写的不对..求表 ...

  7. Android UID 机制

    UID一般理解为User Identifier,在linux中就是用户的ID,表明是哪个用户运行了这个程序.它们主要用于权限的管理. 而在Android 中又有所不同,因为Android为单用户系统, ...

  8. Im4java 操作 ImageMagick 处理图片

    背景 之前用的是JMagick,各种限制各种坑,直到使用了Im4java,真是相当的好用啊. 项目描述 ImageMagic的安装可参考:图片处理软件 ImageMagick 的安装和使用 Im4ja ...

  9. Java变量与运算

    变量 1.变量名可以使用 数字.字母.下划线.$符号.数字包括 '0'~'9' 和某种语言中表示数字的任何 Unicode 字符.字母包括 'A'~'Z'.'a'~'z' 和某种语言中表示字母的任何 ...

  10. Java 使用 Map 实现缓存工具

    以下代码参考于网上,做了小部分修改. 该代码实现了定时清除临时缓存的功能. 缓存管理类 package com.wbproject.util.cache; import java.time.Local ...