美文网首页
C#,面试题

C#,面试题

作者: 青月教主 | 来源:发表于2019-12-09 03:39 被阅读0次

    正文

    一、基础知识

    1、值类型与引用类型

    值类型:struct、enum、int、float、char、bool、decimal

    引用类型:class、delegate、interface、array、object、string

    2、装箱与拆箱

    装箱:把值类型转换成引用类型

    拆箱:把引用类型转换成值类型

    装箱:对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。

    (1)第一步:新分配托管堆内存(大小为值类型实例大小加上一个方法表指针。

    (2)第二步:将值类型的实例字段拷贝到新分配的内存中。

    (3)第三步:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。

    拆箱:检查对象实例,确保它是给定值类型的一个装箱值。将该值从实例复制到值类型变量中。

    在装箱时是不需要显式的类型转换的,不过拆箱需要显式的类型转换。

    3、堆和栈

    存放在栈中时要管存储顺序,保持着先进后出的原则,他是一片连续的内存域,有系统自动分配和维护;

    堆是无序的,他是一片不连续的内存域,有用户自己来控制和释放,如果用户自己不释放的话,当内存达到一定的特定值时,通过垃圾回收器(GC)来回收。

    栈内存无需我们管理,也不受GC管理。当栈顶元素使用完毕,立马释放。而堆则需要GC清理。

    使用引用类型的时候,一般是对指针进行的操作而非引用类型对象本身。但是值类型则操作其本身。

    4、GC(Garbage Collection)

    当程序需要更多的堆空间时,GC需要进行垃圾清理工作,暂停所有线程,找出所有无被引用的对象,进行清理,并通知栈中的指针重新指向地址排序后的对象。

    GC只能处理托管内存资源的释放,对于非托管资源则不能使用GC进行回收,必须由程序员手动回收,例如FileStream或SqlConnection需要调用Dispose进行资源的回收。

    5、CLR(Common Language Runtime)

    公共语言运行库,负责资源管理(包括内存分配、程序及加载、异常处理、线程同步、垃圾回收等),并保证应用和底层操作系统的分离。

    6、静态构造函数

    最先被执行的构造函数,且在一个类里只允许有一个无参的静态构造函数

    执行顺序:静态变量>静态构造函数>实例变量>实例构造函数

    7、文件I/O

    通过流的方式对文件进行读写操作

    (1)FileStream

    (2)StreamReader/StreamWriter

    8、序列化与反序列化

    序列化:将对象状态转换为可保持或传输的格式的过程。将对象实例的字段及类的名称转换成字节流,然后把字节流写入数据流

    反序列化:将流转换为对象。

    这两个过程结合起来,可以轻松地存储和传输数据。

    9、线程同步

    (1)方法一:阻塞(调用Sleep()或Join())

    (2)方法二:加互斥锁lock

    (3)方法三:信号和句柄(AutoResetEvent/ManualResetEvent,调用Set()和WaitOne())

    10、抽象类abstract class与接口interface的异同

    相同点:

    (1)都可以被继承

    (2)都不能被实例化

    (3)都可以包含方法的声明

    不同点:

    (1)抽象类被子类继承;接口被类实现

    (2)抽象类只能被单个类继承;接口可继承接口,并可多继承接口

    (3)抽象基类可以定义字段、属性、方法实现;接口只能定义属性、索引器、事件、和方法声明,不能包含字段

    (4)抽象类可以做方法声明,也可做方法实现;接口只能做方法声明

    (5)具体派生类必须覆盖(override)抽象基类的抽象方法;派生类必须实现接口的所有方法

    (6)抽象类是一个不完整的类,需要进一步细化;接口是一个行为规范

    (7)抽象类中的虚方法或抽象方法必须用public修饰;接口中的所有成员默认为public,不能有private修饰符(也不能用public进行显示修饰)

    11、类class与结构体struct的异同

    Class属于引用类型,是分配在内存的堆上的;

    Struct属于值类型,是分配在内存的栈上的;不能从另外一个结构或者类继承,本身也不能被继承;没有默认的构造函数,但是可以添加构造函数;可以不使用new 初始化

    12、using关键字的使用场景

    (1)作为指令:用于导入其他命名空间中定义的类型或为命名空间创建别名

    (2)作为语句:用于定义一个范围,在此范围的末尾将释放对象

    13、new关键字的使用场景

    (1)实例化对象

    (2)隐藏父类方法

    14、委托与事件

    委托可以把一个方法作为参数传入另一个方法,可以理解为指向一个函数的引用;

    事件是一种特殊的委托。

    15、重载(overload)与重写(override)的区别

    重载:是方法的名称相同,参数或参数类型不同;重载是面向过程的概念。

    重写:是对基类中的虚方法进行重写。重写是面向对象的概念。

    16、return执行顺序

    try{} 里有一个return语句,那么finally{} 里的code在return前执行。

    17、switch(expression)

    其中expression支持任何数据类型,包括null。

    18、反射Reflection

    动态获取程序集信息。

    19、property与attribute的区别

    property是属性,用于存取类的字段;

    attribute是特性,用来标识类,方法等的附加性质。

    20、访问修饰符

    (1)public 公有访问,不受任何限制。

    (2)private 私有访问,只限于本类成员访问。

    (3)protected 保护访问,只限于本类和子类访问。

    (4)internal 内部访问,只限于当前程序集内访问。

    21、static关键字的应用

    对类有意义的字段和方法使用static关键字修饰,称为静态成员,通过类名加访问操作符“.”进行访问; 对类的实例有意义的字段和方法不加static关键字,称为非静态成员或实例成员。

    注: 静态字段在内存中只有一个拷贝,非静态字段则是在每个实例对象中拥有一个拷贝。而方法无论是否为静态,在内存中只会有一份拷贝,区别只是通过类名来访问还是通过实例名来访问。

    22、文件编码格式

    阶段一:ASCII

    阶段二:ANSI(本地化) 如:GBK、GB2312

    阶段三:UNICODE(国际化) 如:UTF-8

    23、值传递与引用传递

    值传递时,系统首先为被调用方法的形参分配内存空间,并将实参的值按位置一一对应地复制给形参,此后,被调用方法中形参值得任何改变都不会影响到相应的实参;

    引用传递时,系统不是将实参本身的值复制后传递给形参,而是将其引用值(即地址值)传递给形参,因此,形参所引用的该地址上的变量与传递的实参相同,方法体内相应形参值得任何改变都将影响到作为引用传递的实参。

    简而言之,按值传递不是值参数是值类型,而是指形参变量会复制实参变量,也就是会在栈上多创建一个相同的变量。而按引用传递则不会。可以通过 ref 和 out 来决定参数是否按照引用传递。

    24、参数传递 ref 与 out 的区别

    (1)ref指定的参数在函数调用时必须先初始化,而out不用

    (2)out指定的参数在进入函数时会清空自己,因此必须在函数内部进行初始化赋值操作,而ref不用

    总结:ref可以把值传到方法里,也可以把值传到方法外;out只可以把值传到方法外

    注意:string作为特殊的引用类型,其操作是与值类型看齐的,若要将方法内对形参赋值后的结果传递出来,需要加上ref或out关键字。

    25、浅克隆与深克隆(浅拷贝与深拷贝)

    (1)浅克隆

    在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制,如图:

    通过实现ICloneable接口的Clone()方法,并调用MemberwiseClone()方法来实现浅克隆

    (2)深克隆

    在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制,如图:

    在C#语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

    二、数据库

    1、数据库操作的相关类

    特定类:Connection,Command,CommandBuilder,DataAdapter,DataReader,Parameter,Transaction

    共享类:DataSet,DataTable,DataRow,DataColumn,DataRealtion,Constraint,DataColumnMapping,DataTableMapping

    (1)Connection:开启程序与数据库之间的连接。

    (2)Command:对数据库发送一些指令。例如增删改查等指令,以及调用存在数据库中的存储过程等。

    (3)DataAdapter:主要在数据源及DataSet 之间执行传输工作,通过Command 下达命令后,将取得的数据放进DataSet对象中。

    (4)DataSet:这个对象可视为一个暂存区(Cache),可以把数据库中所查询到的数据保存起来,甚至可以将整个数据库显示出来,DataSet是放在内存中的。

    备注:将DataAdapter对象当做DataSet 对象与数据源间传输数据的桥梁。DataSet包含若干DataTable、DataTableTable包含若干DataRow。

    (5)DataReader:一笔向下循序的读取数据源中的数据。

    总结:http://ADO.NET 使用Connection对象来连接数据库,使用Command或DataAdapter对象来执行SQL语句,并将执行的结果返回给DataReader或DataAdapter,然后再使用取得的DataReader或DataAdapter对象操作数据结果。

    2、事务

    3、索引

    4、视图

    5、存储过程

    三、数据结构(常用的排序算法)

    1、冒泡排序

    (1)原理

    (2)实现代码

    2、快速排序

    (1)原理

    (2)实现代码

    四、设计模式

    1、单例模式

    在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。保证一个类仅有一个实例,并提供一个该实例的全局访问点。

    (1)经典模式--单线程

    对于线程来说不安全;但在单线程中已满足要求。

    (2)懒汉模式--多线程

    多线程安全;线程不是每次都加锁,节省了性能开销。

    (3)饿汉模式--多线程

    利用静态变量去实现单例,由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次;饿汉模式中的静态变量是随着类加载时被完成初始化的,静态代码块也会随着类的加载一块执行,哪怕你一直没有调用。

    五、软件开发流程

    需求分析 --> 概要设计 --> 详细设计 --> 编码 --> 测试 --> 交付 --> 验收 --> 维护

    六、其他(了解)

    1、.NET Core 与 .NET Framework 的区别

    .NET Core 就是 .NET Framework 的开源且跨平台版本。但微软毕竟不能维护两个不同的分支,一个跑在Windows上,一个跑在Linux(Unix Like)系统上,所以微软抽象出来一个标准库.NET Standard Library,.NET Core 与 .NET Framework 都必须实现标准库的API ,就这样.NET Core、.NET Framework、Xamarin成了三兄弟,分别为不同的平台服务。

    相关文章

      网友评论

          本文标题:C#,面试题

          本文链接:https://www.haomeiwen.com/subject/wygvgctx.html