当前位置:   article > 正文

面试题——C#(一)_c#面试

c#面试

面向对象的三大特征

封装 :封装就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
Public – 允许任何人来访问
Internal – 在同一项目中均可访问
Protected – 继承时子类可以对基类有访问权
Private --私有的,只允许同类中的成员所访问

继承:继承就是在已存在的类基础上,建立一个新类,保留原有类的属性和方法,同时又可以增加新的内容。C#只允许单继承。意思就是只能一对一继承。
public sealed class XX修饰符的子类是不能被继承的。

多态:它是继承性的进一步扩展。没有继承就没有多态。多态性通过派生类覆写基类中的虚函数方法来实现。
父类行为由子类具体实现,包含virtual虚方法,abstract抽象方法,interface接口

重载和重写区别

重载:允许我们在同一个类中定义多个方法名相同,参数列表(参数个数,参数类型,参数顺序)不同的方法。
重写:子类中为满足自己的需要来重复定义某个方法的不同实现,需要用 override 关键字,被重写的方法必须是虚方法,用的是 virtual 关键字。
区别:1.重载在同类中,重写在父子类中。
2.定义方式不同,重载方法名相同参数列表不同,重写方法名和参数列表都相同。
3.调用方式不同,重载使用相同对象以不同参数调用,重写用不同对象以相同参数调用。
4.多态时机不同,重载时编译时多态,重写是运行时多态。

什么是里氏替换原则?(C#多态)

里氏替换原则(Liskov Substitution Principle LSP)⾯向对象设计的基本原则之⼀。
里氏替换原则中说,任何基类可以出现的地⽅,⼦类⼀定可以出现,作⽤⽅便扩展功能能
子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
子类中可以增加自己特有的方法。
当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

值类型与引用类型

值类型存储在栈中(值类型存的是值,存在栈里面)。包含了所有简单类型(int、float、bool、char)、struct、enum。值类型的基类是 System.ValueType
引用类型存储在堆中(引用类型先栈中存一个地址,指向堆存储的位置。回收的时候先回收栈里面的地址,堆中等待CG回收,也可以自己调用垃圾回收机制)。引用类型包含了string,object,class,interface,delegate,array。引用类型的基类是System.Object
结构体是值类型,它里面的引用类型存储在堆里面,也是在栈里面存储一个地址,指向堆
类是引用类型,它里面的值类型也存储在堆里面
拷贝策略:值类型是拷贝数据,引用类型是拷贝引用地址

装箱和拆箱的区别

值类型和引用类型的最终基类是Object(从堆存储空间里面拿出来存到栈里,从栈存储空间里面拿出来存到堆里)
装箱:值类型转换成引用类型的过程,生成新的引用
拆箱:引用类型转换成值类型的过程
避免装箱操作,生成新的应用,解决办法就是第一是重载,第二是泛型

在类的构造函数前加上 static 会报什么错?为什么?

静态构造函数不允许添加访问修饰符,且必须无参数
原因:无论创建多少类型的对象,静态构造函数只执行一次
类实例化或者首静态成员调用之前,运行库会先调用静态构造函数
静态构造函数优先级高于任何其它构造函数
也无法使用this和base来调用静态构造函数
一个类只能有一个静态函数,如果有静态变量,系统也会自动生成静态函数

结构体和类有什么区别

1,结构是实值类型(Value Types),而类则是引用类型(Reference Types)。
2,结构使用栈存储(Stack Allocation),而类使用堆存储(Heap Allocation)。
3,所有结构成员默认都是Public,而类的变量和常量数则默认位Private,不过其他类成员默认都是Public。
4,结构成员不能被声明位Protected,而类成员可以。
5,结构变量声明不能指定初始值、使用New关键字或对数组进行初始化,但是类变量声明可以。
6,结构不能声明默认的构造函数,也就是不拥有参数的非共享构造函数,但是类则无此限制。
7,二者都可以拥有共享构造函数,结构的共享构造函数不能带有参数,但是类的共享构造函数则可以带或者不带参数。
8,结构不允许声明析构函数(Destructor),类则无此限制。
9, 结构永远不会终止,因此CLR不会在任何结构上调用Finalize方法。类则是由内存回收进程加以终止,当内存回收进程检测到没有任何作用的类时,它就会调用类的Finalize方法。
10,结构的实例(Instance)声明,不允许对包含的变量进行初始化设定,类则可以在声明类的实例时,同时进行变量初始化。
11,结构不需要构造函数,类则需要构造函数。
12,结构是隐式继承自ValueType类,而且不能继承任何其他类型,类则可以继续自ValueType以外的任何类。
13,结构是无法被继承的,类则可以。
14,结构只能在一种情况下使用非共享构造函数,那就是非共享构造函数会接受参数。但是类则无此限制,它可以使用带参数或不带参数的非共享构造函数。
15,每一个结构都具有无参数的隐含公共构造函数,此构造函数会将结构的所有成员初始化为其默认值。不需要重新定义这个行为。

什么是接口,描述一下接口的成员具体实现

接口interface,不能定义字段,可以定义【非静态的】属性、索引器、事件、方法
默认public,但不能写任何访问修饰符
接口是引用类型,可以通过as运算符强转,获取某对象的接口的引用
接口可以继承N个接口,继承类要实现所有接口的方法
继承接口的派生类要实现接口的所有方法。
接口和抽象类是不能被实例化的对象(引用类型)

请描述interface和抽象类之间的不同

接口是一种行为,抽象类是一种不能实例化的对象。
接口interface可以定义方法、属性、索引器、事件
抽象类abstract可以定义字段、静态字段和方法、抽象方法、属性、构造函数
接口可以继承多个接口,抽象类只能继承一个类
接口直接实现所有成员,抽象类重写override抽象方法
接口和抽象都不能被实例化,派生类必须实现基类或接口的方法
抽象类可以派生自另一个抽象类,接口可以多重实现,抽象类只能单一继承

请说说你认为C#中 == 和 Equals 的区别是什么?

1.== 是运算符,Equals是万物之父Object中的虚方法,子类可重写
2.Equals 一般在子类中重写后用于比较两个对象中内容是否相同 == 在没有运算符重载的前提下时
引用类型用于比较地址;值类型用于比较值是否相同
3.运算效率不同,一般Equals没有 == 效率高,因为一般Equals比较的内容比==多

请说明字符串中string str = null;string str = “”;string str = string.Empty三者的区别

str = null 在堆中没有分配内存地址
str = “” 和 string.Empty 一样都是在堆内存中分配了空间,里面存储的是空字符串
而string.Empty是一个静态只读变量

C#中四种访问修饰符是哪些?各有什么区别?

属性修饰符:

Serializable:按值将对象封送到远程服务器。
STATread:是单线程套间的意思,是⼀种线程模型。
MATAThread:是多线程套间的意思,也是⼀种线程模 型。

存取修饰符:

public:存取不受限制。
private:只有包含该成员的类可以存取。
internal:只有当前⼯程可以存取。
protected:只有包含该成员的类以及派⽣类可以存 取。

类修饰符:

abstract:抽象类。指示⼀个类只能作为其它类的基 类。
sealed:密封类。指示⼀个类不能被继承。理所当 然,密封类不能同时⼜是抽象类,因为象总是希望 被继承的。

成员修饰符:

abstract:指示该⽅法或属性没有实现。
sealed:密封⽅法。可以防⽌在派⽣类中对该⽅法的
override(重载)。不是类的每个成员⽅法都可以作为 密封⽅法密封⽅法,必须对基类的虚⽅法进⾏᯿载, 提供具体的实现⽅法。所以,在⽅法的声明中,
override:重写。对由基类继承成员的新实现。
sealed修饰符总是和override修饰符同时使⽤。
delegate:委托。⽤来定义⼀个函数指针。C#中的事 件驱动是基于delegate + event的。
const:指定该成员的值只读不允许修改。
event:声明⼀个事件。
extern:指示⽅法在外部实现。
readonly:指示⼀个域只能在声明时以及相同类的内 部被赋值。
static:指示⼀个成员属于类型本身,⽽不是属于特定 的对象。即在定义后可不经实例化,就可使⽤。
virtual:指示⼀个⽅法或存取器的实现可以在继承类中 被覆盖。
new:在派⽣类中隐藏指定的基类成员,从⽽实现重 写的功能。 若要隐藏继承类的成员,请使⽤相同名称 在派⽣类中声明该成员,并⽤ new 修饰符修饰它。

C#中有哪些常用的容器类,各有什么特点

Array(数组):声明double[] balance = new double[10],声明时需要数组长度
ArrayList :声明ArrayList al = new ArrayList(),里面可以存放Object
List(列表):声明List scoreList = new List(),相当于ArrayList 的扩展,声明时添加了泛型参数,可有效减少拆箱装箱操作(本质是数组)

LinkList(双向链表):

1、数组和List、ArrayList集合都有一个重大的缺陷,就是从数组的中间位置删除或插入一个元素需要付出很大的代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。
2、LinkedList(底层是由链表实现的)基于链表的数据结构,很好的解决了数组删除插入效率低的问题,且不用动态的扩充数组的长度。
3、LinkedList的优点:插入、删除元素效率比较高;缺点:访问效率比较低。
Hashtable(哈希表):表示键/值对的集合。Hashtable中key-value键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对,任何非 null 对象都可以用作键或值。
Dictionary(字典):表示键/值对的集合。Dictionary<string, string>是一个泛型,相当于## ## Hashtable的扩展
Stack栈:先进后出,入栈和出栈,底层泛型数组实现,入栈动态扩容2倍
Queue队列:先进先出,入队和出队,底层泛型数组实现,表头表尾指针,判空还是满通过size比较

性能排序:

插入性能: LinkedList > Dictionary > HashTable > List
遍历性能:List > LinkedList > Dictionary > HashTable
删除性能: Dictionary > LinkedList > HashTable > List

字典Dictionary的内部实现原理

泛型集合命名空间using System.Collections.Generic; 任何键都必须是唯一
该类最大的优点就是它查找元素的时间复杂度接近O(1),实际项目中常被用来做一些数据的本地缓存,提升整体效率。
实现原理
哈希算法:将不定长度的二进制数据集给映射到一个较短的二进制长度数据集一个Key通过HashFunc得到HashCode
Hash桶算法:对HashCode进行分段显示,常用方法是对HashCode直接取余
解决碰撞冲突算法(拉链法):分段会导致key对应的桶会相同,拉链法的思想就像对冲突的元素,建立一个单链表,头指针存储到对应的哈希桶位置。反之就是通过确定hash桶位置后,遍历单链表,获取对应的value

Heap与Stack有何区别?

heap是堆,stack是栈。
stack的空间由操作系统自 动分配和释放,heap的空间是手动申请和释放的, heap常用new关键字来分配。
stack空间有限,heap 的空间是很大的自由区。

Mock和Stub有何区别?

Mock与Stub的区别:Mock:关注行为验证。细粒度的 测试,即代码的逻辑,多数情况下用于单元测试。 Stub:关注状态验证。粗粒度的测试,在某个依赖系 统不存在或者还没实现或者难以测试的情况下使用, 例如访问文件系统,数据库连接,远程协议等。

为什么dynamic font 在 unicode环境下优于 staticfont(字符串编码)

Unicode是国际组织制定的可以容纳世界上所有⽂字和符号的字符编码⽅案。
使⽤动态字体时,Unity将不会预先⽣成⼀个与所有字体的字符纹理。
当需要⽀持亚洲语⾔或者较⼤的字体的时候,若使⽤正常纹理,则字体的纹理将⾮常⼤

string和stringbuilder和stringBuffer区别

String不变性,字符序列不可变,对原管理中实例对象赋值,会重新开一个新的实例对象赋值,新开的实例对象会等待被GC。
string拼接要重新开辟空间,因为string原值不会改变,导致GC频繁,性能消耗大
StringBuffer是字符串可变对象,可通过自带的StringBuffer.方法来改变并生成想要的字符串。对原实例对象做拼接的实例,不会生成新的实例对象。拼接使用StringBuilder和StringBuffer,只开辟一个内存空间,这是性能优化的点
StringBuilder是字符串可变对象,基本和StringBuilder相同。唯一的区别是StringBuffer是线程安全,相关方法前带synchronized关键字,一般用于多线程

foreach迭代器遍历和for循环遍历的区别

foreach中的迭代变量item是的只读,不能对其进行修改,foreach对int[]数组循环已经不产生GC,避免对ArrayList进行遍历
for语句中初始化变量i的作用域,循环体内部可见。
通过索引进行遍历,可以根据索引对所遍历集合进行修改

能用foreach遍历访问的对象需要实现___接⼝或声明___⽅法的类型(C#遍历)

IEnumerable;GetEnumerator
List和Dictionary类型可以用foreach遍历,他们都实现了IEnumerable接口,申明了GetEnumerator方法。

请简述GC垃圾管理器,和GC产生的原因,并描述如何避免

GC垃圾回收机制,避免堆内存溢出,定期回收那些没有有效引用的对象内存
GC优化,就是优化堆内存,减少堆内存,即时回收堆内存
GC归属于CLR
垃圾回收流程:引用类型的变量声明之后,在栈里面存储地址,把数据存在堆中(如A堆)。等到A空间满了之后,首先会把堆里面没有地址指向得分数据删除,没有删除的数据放进级别更高的堆空间中(如B)。等待B空间满了之后在进行下次垃圾回收。也可以自己调用垃圾回收方法System.GC.Collect();

内存泄漏指什么?常见的内存泄漏有哪些?

内存泄漏指的就是对象超过生命周期后而不能被GC回收,一般指不会再使用的引用对象由于某些操作而不能被GC垃圾回收,而一直占用着内存
更风趣通俗一点的说就是:没用的家伙没有被当成垃圾回收
常见的内存泄漏有:
1.静态引用
2.不使用的引用对象没有置null,一直被引用
3.文件操作时,没有使用using或者没有进行Dispose()
4.委托或事件注册后没有解除注册(有加就有减)
等等

如何避免
1.减少new的次数
2.字符串拼接使用stringbuilder,字符串比较先定义一个变量存储,防止产生无效内存
3.list,new时候,规定内存大小
4.如果要射线检测,应该使用避免GC的方法XXXXNoAlloc函数
5.使用静态变量,GC不会回收存在的对象,但静态变量的引用对象可能被回收
6.使用枚举替代字符串变量
7.调用gameobject.tag=="XXX"就会产生内存垃圾;那么采用GameObject.CompareTag()可以避免内存垃圾的产生:
8.不要在频繁调用的函数中反复进行堆内存分配,比如OnTriggerXXX,Update等函数
9.在Update函数中,运行有规律的但不需要每一帧执行的代码,可以使用计时器,比如1秒执行一次某些代码!!

泛型是什么

多个代码对 【不同数据类型】 执行 【相同指令】的情况
泛型:多个类型共享一组代码
泛型允许类型参数化,泛型类型是类型的模板
5种泛型:类、结构、接口、委托、方法
类型占位符 T 来表示泛型

泛型的约束有哪几种?

1.值类型约束 T:struct
2.引用类型约束 T:class
3.公共无参构造约束 T:new()
4.类约束 T:类名
5.接口约束 T:接口名
6.另一个泛型约束 T:U

委托

定义:委托类似于一种安全的指针引用,在使用它时是 当做类来看待而不是一个方法,相当于对一组方 法的列表的引用。
个人理解:委托就是一个存放函数的容器
delegate :声明委托类型:delegate void Del(string str);
实例化委托:Del del1 = new Del(Notify);
多播委托就是委托里面加了多个方法
Action是无返回值的泛型委托。
至少1个参数,无返回值
Action<int,string> 表示有传入参数int,string无返回值的委托
Func是有返回值的泛型委托
至少0个参数,根据返回值泛型返回。必须有返回值,不可void
Func 表示无参,返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
predicate 是返回bool型的泛型委托
predicate至少1个参数,至多1个参数,返回值固定为bool
predicate 表示传入参数为int 返回bool的委托。
匿名委托
delegate(int x, int y){}
Lambda表达式
(int x, int y)=>{}
如果有返回值直接在方法体里面return
In out修饰泛型委托
Ont 协变 只能修饰返回值
In 逆变 只能修饰参数

事件(event)

C#中使用事件需要的步骤:
1、创建一个委托;
2、将创建的委托与特定事件关联;
3、编写C#事件处理程序;
4、利用编写的C#事件处理程序生成一个委托实例;
5、把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件。
public delegate void BoilerLogHandler(string status);
public event BoilerLogHandler BoilerEventLog;

委托与事件的区别

事件是委托类型的一个实例,加上了event的权限控制,限制权限,只允许在事件声明类里面去invoke和赋值,不允许外面,甚至子类调用。但是可以在外面添加(或减少)绑定的方法

概述c#中代理和事件?

代理就是⽤来定义指向⽅法的引⽤。
C#事件本质就是对消息的封装,⽤作对象之间的通信;发送⽅叫事件发送器,接收⽅叫事件接收器;

委托与接口的区别

接口(interface)是约束类应该具备的功能集合,约束了类应该具备的功能,使类从千变万化的具体逻辑中解脱出来,便于类的管理和扩展,同时又合理解决了类的单继承问题
C#中的委托 是约束方法集合的一个类,可以便捷的使用委托对这个方法集合进行操作。

C#中ref和out关键字有什么区别?知道Ref的深层原理是什么?

ref修饰引用参数。参数必须赋值,带回返回值,又进又出
out修饰输出参数。参数可以不赋值,带回返回值之前必须明确赋值,引用参数和输出参数不会创建新的存储位置
如果ref参数是值类型,原先的值类型数据,会随着方法里的数据改变而改变,
如果ref参数值引用类型,方法里重新赋值后,原对象堆中数据会改变,如果对引用类型再次创建新对象并赋值给ref参数,引用地址会重新指向新对象堆数据。方法结束后形参和新对象都会消失。实参还是指向原始对象,值不够数据改变了

什么是闭包?可以举例说明

闭包是指有权访问另一个函数作用域中的变量的函数
所以闭包一般都是指的一个函数
创建这种特殊闭包函数的方式往往是在一个函数中创建另一个函数

C#中unsafe关键字是用来做什么的?什么场合下使用?

unsafe 非托管代码,配合fixed一起使用 ,用在需要指针操作的场合
项目背包系统的任务装备栏使用到

C#引用和C++指针的区别

C#不支持指针,但可以使用Unsafe,不安全模式,CLR不检测
C#可以定义指针的类型、整数型、实数型、struct结构体
C#指针操作符、C#指针定义
使用fixed,可以操作类中的值类型
相同点:都是地址
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
指针是个实体,引用是个别名。
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
引用是类型安全的,而指针在不安全模式下

请简要说明.Net跨平台原理

由于.Net Framework中利用CLI和CLR实现了跨语言,CLR主要起到一个翻译、运行、管理中间代码的作用
.Net Core和Mono就是利用了CLR的这一特点,为不同操作系统实现对应CLR(公共语言运行时或.Net虚拟机)
那么不同操作系统对应的CLR就会将IL中间代码翻译为对应系统可以执行的原生代码(机器码)达到跨平台的目的

.Net 与 Mono 的关系?

.Net是一个语言平台
Mono为.Net提供集成开发环境,集成并实现了
.NET的编译器、CLR 和基础类库,
使得.Net既可以运行在windows也可以运行于 linux,Unix,Mac OS 等。

反射实现原理

反射概念:程序运行的时候可以查看其他程序集或者自身的元数据(程序运行时候通过反射得到自身或者其他程序集代码的各种信息。类,函数,变量等等)
另一种定义:审查元数据并收集元数据的信息。
元数据:编译后的最基本数据单元,就是一堆表,反射就是解析这些元数据。
反射是在运行期间获取到类、对象、方法、数据的一种手段
主要使用类库System.Reflection
反射要点:如何获取类型,根据类型来动态创建对象,反射获取方法以及动态调用方法,动态创建委托

一、动态获取类型信息

1.System.Reflection.Assembly.Load(“XXXX.dll”) 动态加载程序集
2.System.Type.GetType(“XXXX类名”); //动态获取某程序集中某类信息
3.obj.GetType(); //已知对象获取类信息 ——或者——typeof(类型) //已知类类型

二、动态创建对象实例(上一步操作后获得类对象)

System.Activator.CreateInstance(Type type);

三、动态访问成员调用方法(上一步操作后已获取实例对象)

System.Reflection.MethodInfo method = type.GetMethod(“方法名”);//获得方法
System.Reflection.MethodInfo.Invoke(object , new object[]{参数}) //调用的类实例和实例参数

核心类

System.Reflection.Assembly 描述程序集
System.Type 描述类
System.Reflection.FieldInfo 描述了类的字段
System.Reflection.ConstructorInfo 描述构造函数
System.Reflection.MethodInfo 描述类的方法
System.Reflection.PropertyInfo 描述类的属性

Unity引擎中哪些功能使用了C#的反射功能?至少说出一点

1.Inspector窗口中显示的内容
2.预设体文件
3.场景文件
4.Unity中的各种特性
等等

MVC

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。
用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
  通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
  通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
  通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

源语言程序:某种城西设计语言写成的,比如C#,C,Java等语言程序(翻译成伪代码才能用)

目标语言程序:二进制表示的伪机器代码写的程序
预处理器指令:在实际编辑开始前对信息进行预处理,都是以#开始的
如:#region #endregion折叠代码 #define #enddefine 定义一个符号 #if #warning #error 在unity中判断使用的版本
程序集:程序集由编辑器编译获得是写的所有的代码的集合,一般表现为dll exe
元数据:就是用来描述数据的数据。如程序中的类,函数,变量等等有关程序类型数据

序列化是什么?常见的序列化方式有哪些?什么时候我们会用到序列化?

序列化是将程序中数据对象转换为可以存储或传输的形式 的过程。
举例:
比如我们常见的序列化方式 xml、Json、2进制等。就是将内存中的数据按照我们自己定义的规则进行序列化,序列化之后就可以用于存储和传输,当读取和接受数据时,只需要按照对应规则进行反序列化便可得到原始数据

所谓的存储读取和传输接受,其实一般指的就是数据持久化和网络通讯
所以我们经常会在这两块知识点看到序列化反序列化这两个关键词

引用类型?.值(如果引用类型为null就不执行后面方法,要是不为null就继续执行)

A = a ?? 100; 意思是a如果是空的就A=100,反之A = a

内插字符串 美元的符号 str = $”我的名字是{name},年龄{age}”

Public int add(int x, int y) => x+y (输入xy值返回x+y的值)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/756240
推荐阅读
相关标签
  

闽ICP备14008679号