值类型与引用类型

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

  • 值类型包括简单类型、结构体类型和枚举类型,引用类型包括自定义类、数组、接口、委托等。
  • 赋值方式:将一个值类型变量赋给另一个值类型变量时,将复制包含的值。这与引用类型变量的赋值不同,引用类型变量的赋值只复制对象的引用(即内存地址,类似C++中的指针),而不复制对象本身。
  • 继承:值类型不可能派生出新的类型,所有的值类型均隐式派生自 System.ValueType。但与引用类型相同的是,结构也可以实现接口。
  • null:与引用类型不同,值类型不可能包含 null 值。然而,可空类型功能允许将 null 赋给值类型。
  • 每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值,值类型初始会默认为0,引用类型默认为null。
  • 值类型存储在栈中,引用类型存储在托管堆中。

2. 结构和类的区别?
结构体是值类型,类是引用类型,主要区别如上述。其他的区别:
结构不支持无参构造函数,不支持析构函数,并且不能有protected修饰
结构常用于数据存储,类class多用于行为
class需要用new关键字实例化对象,struct可以不使用new关键字
class可以为抽象类,struct不支持抽象

3. delegate是引用类型还是值类型?enum、int[]和string呢?
enum枚举是值类型,其他都是引用类型。

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

5.“结构”对象可能分配在堆上吗?什么情况下会发生,有什么需要注意的吗?
结构是值类型,有两种情况会分配在堆上面:
结构作为class的一个字段或属性,会随class一起分配在堆上面;
装箱后会在堆中存储,尽量避免值类型的装箱,值类型的拆箱和装箱都有性能损失。

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

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

8.有几种方法可以判定值类型和引用类型?
简单来说,继承自System.ValueType的是值类型,反之是引用类型。

9. C#支持哪几个预定义的值类型?支持哪些预定义的引用类型?
值类型:整数、浮点数、字符、bool和decimal
引用类型:Object,String

10. 说说值类型和引用类型的生命周期?
值类型在作用域结束后释放,引用类型由GC垃圾回收器回收。

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

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

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

装箱与拆箱

1.什么是拆箱和装箱?装箱就是值类型转换为引用类型,拆箱就是引用类型(被装箱的对象)转换为值类型。
2.什么是箱子?就是引用类型对象。
3.箱子放在哪里?托管堆上。
4.装箱和拆箱有什么性能影响?装箱和拆箱都涉及到内存的分配和对象的创建,有较大的性能影响。
5.如何避免隐身装箱?编码中,多使用泛型、显示装箱。
6.箱子的基本结构?
上面说了,箱子就是一个引用类型对象,因此它的结构,主要包含两部分:
a.值类型字段值;
b.引用类型的标准配置,引用对象的额外空间:TypeHandle和同步索引块。
7.装箱的过程?

  1. 在堆中申请内存,内存大小为值类型的大小,再加上额外固定空间(引用类型的标配:TypeHandle和同步索引块);
  2. 将值类型的字段值(x=1023)拷贝新分配的内存中;
  3. 返回新引用对象的地址(给引用变量object o)

8.拆箱的过程?

  1. 检查实例对象(object o)是否有效,如是否为null,其装箱的类型与拆箱的类型(int)是否一致,如检测不合法,抛出异常;
  2. 指针返回,就是获取装箱对象(object o)中值类型字段值的地址;
  3. 字段拷贝,把装箱对象(object o)中值类型字段值拷贝到栈上,意思就是创建一个新的值类型变量来存储拆箱后的值。

string与字符串操作

1.字符串是引用类型类型还是值类型?引用类型。
2.在字符串连加处理中,最好采用什么方式,理由是什么?
少量字符串连接,使用String.Concat,大量字符串使用StringBuilder,因为StringBuilder的性能更好,如果string的话会创建大量字符串对象。
3.使用 StringBuilder时,需要注意些什么问题?
少量字符串时,尽量不要用,StringBuilder本身是有一定性能开销的;
大量字符串连接使用StringBuilder时,应该设置一个合适的容量。

类与接口

1. 所有类型都继承System.Object吗?
基本上是的,所有值类型和引用类型都继承自System.Object,接口是一个特殊的类型,不继承自System.Object。
2. 解释virtual、sealed、override和abstract的区别

  • virtual申明虚方法的关键字,说明该方法可以被重写;
  • sealed说明该类不可被继承;
  • override重写基类的方法;
  • abstract申明抽象类和抽象方法的关键字,抽象方法不提供实现,由子类实现,抽象类不可实例化。

3. 接口和类有什么异同?
不同点:
1、接口不能直接实例化。
2、接口只包含方法或属性的声明,不包含方法的实现。
3、接口可以多继承,类只能单继承。
4、类有分部类的概念,定义可在不同的源文件之间进行拆分,而接口没有(接口也可分部)。
5、表达的含义不同,接口主要定义一种规范,统一调用方法,也就是规范类,约束类,类是方法功能的实现和集合。
相同点:
1、接口、类和结构都可以从多个接口继承。
2、接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。
3、接口和类都可以包含事件、索引器、方法和属性。
4. 抽象类和接口有什么区别?
1、继承:接口支持多继承;抽象类不能实现多继承。
2、表达的概念:接口用于规范,更强调契约,抽象类用于共性,强调父子。抽象类是一类事物的高度聚合,那么对于继承抽象类的子类来说,对于抽象类来说,属于"Is A"的关系;而接口是定义行为规范,强调“Can Do”的关系,因此对于实现接口的子类来说,是"行为需要按照接口来完成"。
3、方法实现:对抽象类中的方法,即可以给出实现部分,也可以不给出;而接口的方法(抽象规则)都不能给出实现部分,接口中方法不能加修饰符。
4、子类重写:继承类对于两者所涉及方法的实现是不同的。继承类对于抽象类所定义的抽象方法,可以不用重写,也就是说,可以延用抽象类的方法;而对于接口类所定义的方法或者属性来说,在继承类中必须重写,给出相应的方法和属性实现。
5、新增方法的影响:在抽象类中,新增一个方法的话,继承类中可以不用作任何处理;而对于接口来说,则需要修改继承类,提供新定义的方法。
6、接口可以作用于值类型(枚举可以实现接口)和引用类型,抽象类只能作用于引用类型。
7、接口不能包含字段和已实现的方法,接口只包含方法、属性、索引器、事件的签名,抽象类可以定义字段、属性、包含有实现的方法。
5. 重载与覆盖的区别?
重载:当类包含两个名称相同但签名不同(方法名相同,参数列表不相同)的方法时发生方法重载。用方法重载来提供在语义上完成相同而功能不同的方法。
覆写:在类的继承中使用,通过覆写子类方法可以改变父类虚方法的实现。
主要区别:
1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系。
2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
3、覆盖要求参数列表相同;重载要求参数列表不同。
4、覆盖关系中,调用那个方法体,是根据对象的类型来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
6. 在继承中new和override相同点和区别?看下面的代码,有一个基类A,B1和B2都继承自A,并且使用不同的方式改变了父类方法Print()的行为。测试代码输出什么?为什么?

 class Program
{
static void Main(string[] args)
{
B1 b1 = new B1();
B2 b2 = new B2();
b1.Print();
b2.Print();//输出 B1、B2
A ab1 = new B1();
A ab2 = new B2();
ab1.Print();
ab2.Print();//输出 B1、A
Console.ReadKey();
}
}
public class A
{
public virtual void Print()
{
Console.WriteLine("A");
}
}
public class B1 : A
{
public override void Print()
{
Console.WriteLine("B1");
}
}
public class B2 : A
{
public new void Print()
{
Console.WriteLine("B2");
}
}

7.class中定义的静态字段是存储在内存中的哪个地方?为什么会说它不会被GC回收?
随类型对象存储在内存的加载堆上,因为加载堆不受GC管理,其生命周期随AppDomain,不会被GC回收。

常量、字段、属性、特性与委托

1. const和readonly有什么区别?
const关键字用来声明编译时常量,readonly用来声明运行时常量。都可以标识一个常量,主要有以下区别:
1、初始化位置不同。const必须在声明的同时赋值;readonly即可以在声明处赋值,也可以在构造方法里赋值。
2、修饰对象不同。const即可以修饰类的字段,也可以修饰局部变量;readonly只能修饰类的字段 。
3、const是编译时常量,在编译时确定该值,且值在编译时被内联到代码中;readonly是运行时常量,在运行时确定该值。
4、const默认是静态的;而readonly如果设置成静态需要显示声明 。
5、支持的类型时不同,const只能修饰基元类型或值为null的其他引用类型;readonly可以是任何类型。

 namespace ConsoleApp
{
class Program
{
public readonly int PORT;
static void Main(string[] args)
{
B a = new B();
Console.WriteLine(a.PORT);
Console.WriteLine(B.PORT2);
Console.WriteLine(a.PORT3.ccc);
Console.ReadKey();
}
}
class B
{
//readonly即可以在声明处赋值,也可以在构造方法里赋值
public readonly int PORT;
//const必须在声明的同时赋值
public const int PORT2 = ;
//public const A PORT3 = new A() error const只能修饰基元类型或值为null的其他引用类型
//readonly可以是任何类型
public readonly A PORT3 = new A();
public B()
{
PORT = ;
}
}
class A
{
public string ccc = "aaaaa";
}
}

2. 字段与属性有什么异同?
•属性提供了更为强大的,灵活的功能来操作字段
•出于面向对象的封装性,字段一般不设计为Public
•属性允许在set和get中编写代码
•属性允许控制set和get的可访问性,从而提供只读或者可读写的功能 (逻辑上只写是没有意义的)
•属性可以使用override 和 new
3. 静态成员和非静态成员的区别?
•静态变量使用 static 修饰符进行声明,静态成员在加类的时候就被加载(上一篇中提到过,静态字段是随类型对象存放在Load Heap上的),通过类进行访问。
•不带有static 修饰符声明的变量称做非静态变量,在对象被实例化时创建,通过对象进行访问 。
•一个类的所有实例的同一静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值 。
•静态函数的实现里不能使用非静态成员,如非静态变量、非静态函数等。
4. 特性是什么?如何使用?
特性与属性是完全不相同的两个概念,只是在名称上比较相近。Attribute特性就是关联了一个目标对象的一段配置信息,本质上是一个类,其为目标元素提供关联附加信息,这段附加信息存储在dll内的元数据,它本身没什么意义。运行期以反射的方式来获取附加信息。
5. C#中的委托是什么?事件是不是一种委托?
什么是委托?简单来说,委托类似于C或C++中的函数指针,允许将方法作为参数进行传递。
•C#中的委托都继承自System.Delegate类型;
•委托类型的声明与方法签名类似,有返回值和参数;
•委托是一种可以封装命名(或匿名)方法的引用类型,把方法当做指针传递,但委托是面向对象、类型安全的;
事件可以理解为一种特殊的委托,事件内部是基于委托来实现的。

GC与内存管理

1. 简述一下一个引用对象的生命周期?
•new创建对象并分配内存
•对象初始化
•对象操作、使用
•资源清理(非托管资源)
•GC垃圾回收
2. GC进行垃圾回收时的主要流程是?
① 标记:先假设所有对象都是垃圾,根据应用程序根Root遍历堆上的每一个引用对象,生成可达对象图,对于还在使用的对象(可达对象)进行标记(其实就是在对象同步索引块中开启一个标示位)。
② 清除:针对所有不可达对象进行清除操作,针对普通对象直接回收内存,而对于实现了终结器的对象(实现了析构函数的对象)需要单独回收处理。清除之后,内存就会变得不连续了,就是步骤3的工作了。
③ 压缩:把剩下的对象转移到一个连续的内存,因为这些对象地址变了,还需要把那些Root跟指针的地址修改为移动后的新地址。
3. GC在哪些情况下回进行回收工作?
•内存不足溢出时(0代对象充满时)
•Windwos报告内存不足时,CLR会强制执行垃圾回收
•CLR卸载AppDomian,GC回收所有
•调用GC.Collect
•其他情况,如主机拒绝分配内存,物理内存不足,超出短期存活代的存段门限
4. using() 语法是如何确保对象资源被释放的?如果内部出现异常依然会释放资源吗?
using() 只是一种语法形式,其本质还是try…finally的结构,可以保证Dispose始终会被执行。
5. 解释一下C#里的析构函数?为什么有些编程建议里不推荐使用析构函数呢?
C#里的析构函数其实就是终结器Finalize,因为长得像C++里的析构函数而已。
有些编程建议里不推荐使用析构函数要原因在于:第一是Finalize本身性能并不好;其次很多人搞不清楚Finalize的原理,可能会滥用,导致内存泄露,因此就干脆别用了。
6. Finalize() 和 Dispose() 之间的区别?
Finalize() 和 Dispose()都是.NET中提供释放非托管资源的方式,他们的主要区别在于执行者和执行时间不同:
•finalize由垃圾回收器调用;dispose由对象调用。
•finalize无需担心因为没有调用finalize而使非托管资源得不到释放,而dispose必须手动调用。
•finalize不能保证立即释放非托管资源,Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间;而dispose一调用便释放非托管资源。
•只有class类型才能重写finalize,而结构不能;类和结构都能实现IDispose。
另外一个重点区别就是终结器会导致对象复活一次,也就说会被GC回收两次才最终完成回收工作,这也是有些人不建议开发人员使用终结器的主要原因。
7. Dispose和Finalize方法在何时被调用?
•Dispose一调用便释放非托管资源;
•Finalize不能保证立即释放非托管资源,Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间;
8. .NET中的托管堆中是否可能出现内存泄露的现象?
是的,可能会。比如:
•不正确的使用静态字段,导致大量数据无法被GC释放;
•没有正确执行Dispose(),非托管资源没有得到释放;
•不正确的使用终结器Finalize(),导致无法正常释放资源;
•其他不正确的引用,导致大量托管对象无法被GC释放;
9. 在托管堆上创建新对象有哪几种常见方式?
•new一个对象;
•字符串赋值,如string s1=”abc”;
•值类型装箱。

多线程编程与线程同步

1. 描述线程与进程的区别?
•一个应用程序实例是一个进程,一个进程内包含一个或多个线程,线程是进程的一部分;
•进程之间是相互独立的,他们有各自的私有内存空间和资源,进程内的线程可以共享其所属进程的所有资源;
2. lock为什么要锁定一个参数,可不可锁定一个值类型?这个参数有什么要求?
lock的锁对象要求为一个引用类型。它可以锁定值类型,但值类型会被装箱,每次装箱后的对象都不一样,会导致锁定无效。
对于lock锁,锁定的这个对象参数才是关键,这个参数的同步索引块指针会指向一个真正的锁(同步块),这个锁(同步块)会被复用。
3. 多线程和异步有什么关系和区别?
多线程是实现异步的主要方式之一,异步并不等同于多线程。实现异步的方式还有很多,比如利用硬件的特性、使用进程或纤程等。在.NET中就有很多的异步编程支持,比如很多地方都有Begin***、End***的方法,就是一种异步编程支持,它内部有些是利用多线程,有些是利用硬件的特性来实现的异步编程。
4. 线程池的优点有哪些?又有哪些不足?
优点:减小线程创建和销毁的开销,可以复用线程;也从而减少了线程上下文切换的性能损失;在GC回收时,较少的线程更有利于GC的回收效率。
缺点:线程池无法对一个线程有更多的精确的控制,如了解其运行状态等;不能设置线程的优先级;加入到线程池的任务(方法)不能有返回值;对于需要长期运行的任务就不适合线程池。
5.Mutex和lock有何不同?一般用哪一个作为锁使用更好?
Mutex是一个基于内核模式的互斥锁,支持锁的递归调用,而Lock是一个混合锁,一般建议使用Lock更好,因为lock的性能更好。

dotNET面试(二)的更多相关文章

  1. [Java面试二]Java基础知识精华部分.

    一:java概述(快速浏览): 1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒.PDA等的微处理器: 1994年将Oak语言更名 ...

  2. java面试二

    技术交流群: 233513714 126.什么是ORM?答:对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题 ...

  3. dotNET面试(三)

    1.简述 private. protected. public. internal 修饰符的访问权限.private : 私有成员, 在类的内部才可以访问 ,也就是类内部的函数等成员可以访问.prot ...

  4. 面试(二)---synchronized

    一.前言        本来计划将ConcurrentHashMap和HashMap对比着来说下,奈何看的源码有点懵逼,我在思考思考,等等有个清晰的思路在搞起来,我们先来谈一下synchronized ...

  5. Android面试二之Fragment

    基本概念 Fragment,简称碎片,是Android 3.0(API 11)提出的,为了兼容低版本,support-v4库中也开发了一套Fragment API,最低兼容Android 1.6. F ...

  6. java笔试面试二

    http://www.cnblogs.com/lanxuezaipiao/p/3371224.html

  7. JAVA基础面试(二)

    11.是否可以从一个static方法内部发出对非static方法的调用? 不可以.因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用 ...

  8. django面试二

    33. django的缓存能使用redis吗?如果可以的话,如何配置? #1.安装 pip install django-redis#2.在stting中配置CACHES,可以设置多个缓存,根据名字使 ...

  9. dotNET面试(一)

    1.列举ASP.NET 页面之间传递值的几种方式. 1).使用QueryString, 如....?id=1; response. Redirect().... 2).使用Session变量 3).使 ...

随机推荐

  1. 解释ARP协议和RARP协议

    解释ARP(地址解析协议) 首先,每个主机都会在自己的ARP缓冲区中建立一个ARP列表,以表示IP地址和MAC地址之间的对应关系. 当源主机要发送数据时,首先检查ARP列表中是否有对应IP地址的目的主 ...

  2. 【LeetCode】随机化算法 random(共6题)

    [384]Shuffle an Array(2019年3月12日) Shuffle a set of numbers without duplicates. 实现一个类,里面有两个 api,struc ...

  3. Sass-减法

    Sass 的减法运算和加法运算类似,我们通过一个简单的示例来做阐述: 同样的,运算时碰到不同类型的单位时,编译也会报错,如:

  4. struts2中的Action实现的三种方式

    Action类创建方式有哪些? 方式一:直接创建一个类,可以是POJO,即原生Java类,没有继承任何类,也没有实现任何接口 这种方式使得strust2框架的代码侵入性更低,但是这种方式是理想状态,开 ...

  5. python基础:3.高级运算符

    1.异或运算 十进制的异或运算,先转成二进制进行异或,按位进行比较,对应位置相同则为0,对应位置不同则为1,,再从异或结果转成十进制. python中: 1 ^ 1 = 0 1 ^ 2 = 3 1 ^ ...

  6. python3-Django初始化项目详细

    0.背景 近期在学习django,在初始化项目的时候遇到了一丢坑,记录一下. 1.安装django 下载安装包解压出来后,python3 setup.py install 即可 2.创建项目 djan ...

  7. DELPHI FMX IOS模拟器调试时出现No SDKs could be found

    解决办法: 在OSX里打开XCODE,​点击XCODE菜单->​Perferences->Locations​在Commond  Line Tools选择XCODE ​

  8. IOC(控制反转)和DI(依赖注入)

    IOC(控制反转): 那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所.婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友 ...

  9. Cloudera Hadoop 环境搭建(离线安装)

    关于CDH和Cloudera Manager CDH (Cloudera's Distribution, including Apache Hadoop),是Hadoop众多分支中的一种,由Cloud ...

  10. spring-boot和redis的缓存使用

    1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.Maven Plugin管理 pom.xml配置代码: <?xml versio ...