C#八股总结
重载和重写的区别
方法重载:在同一个类中定义多个同名但参数不同的方法。
方法重写:通过使用 virtual 和 override 关键字,实现基类和派生类之间的方法重写。
重载发生在同类中,重写发生在父子类中
重载方法名相同参数不同,重写方法名和参数列表都相同
面向对象的三大特点
封装,继承,多态
继承能够提高代码重用度,增强可维护性。继承最主要的作用就是把子类的公共属性集合起来,便于共同管理,使用起来更加方便
继承的传递性:b继承a,c继承b,c也有a的特性
继承的单根性:C#中一个类只能继承一个类,不能有多个父类
继承是实现多态的重要手段,通过基类引用指向派生类对象,可以实现动态绑定。
封装是将数据(属性)和操作数据的方法(行为)相结合,并限制外部直接访问数据的能力。保护对象的内部状态,同时提供公共方法让外部访问,增强数据的安全性(private,public修饰符)
多态性:通过统一的接口来调用不同类的对象,从而实现统一操作有不同的表现形式。两种主要形式为重载和重写
值类型和引用类型的区别
值类型、引用类型是根据数据存储的⻆度来分的)就是值类型用于存储数据的值,引用类型用于存储对实际数据的引用。
值类型包含了所有简单类型(bool,char,int,float,enum,struct),继承自基类System.ValueType
引用类型包含了string,object,class,interface,delegate,继承自基类System.Object
1.值类型变量直接把变量的值保存在堆栈中,引⽤类型的变量把实际数据的地址保存在堆栈中。
2.值存取快,引用类型存取慢
3.值类型表示实际数据,引用类型表示储存在内存堆中的数据的指针和引用
4.值类型的变量直接存放实际的数据,引用类型的变量存放的是数据的地址,即对象的引用
private,public,protected的区别
public:对任何类和成员都公开,无限制访问
private:仅对该类公开
protected:对该类及其派生类公开
ArrayList和List的主要区别
ArrayList 是非泛型集合,存储的数据类型为 object。他可以存储任何类型的对象,但在使用时需要进行类型转换(装箱和拆箱),可能导致运行时错误。
由于需要进行装箱和拆箱,ArrayList 在处理值类型时性能较低。此外,由于其对每个元素都必须进行类型检查,因此在性能上也劣于 List。
List:
List 是泛型集合,提供类型参数 T,确保在编译时就能检查类型,避免了类型转换问题。因此,它更安全且性能更高。
List 提供更好的性能,因为它不需要进行装箱和拆箱,且能够直接操作指定的类型。
GC(垃圾回收)产生的原因,如何避免?
为了避免内存溢出而产生的回收机制
如何避免:
减少new产生对象的次数
使用公共的的对象(静态成员)
将String转换为StringBuilder
interface和抽象类之间的不同
接口是一种完全抽象的类型,定义了一组方法、属性、事件或索引器,但不提供任何实现。
抽象类是一种包含部分实现的类,可以包含抽象方法(没有实现)和具体方法(有实现)。
接口用于定义一组功能,这些功能可以被多个类实现。通过接口,可以确保不同的类具有一致的功能契约。
抽象类用于提供一个基础类,允许子类继承并重用代码,同时也强制子类实现特定的方法。
接口可以多继承,抽象类是单继承(一个类只能继承自一个抽象类)
接口中的成员默认都是 public,不能使用其他访问修饰符(如 private 或 protected)。
抽象类中的成员可以有多种访问修饰符(如 public、protected、private),提供了更灵活的访问控制。
关键字Sealed用在类声明和函数声明时的作用
类声明时可防止其他类继承此类,在方法中声明则可防止派生类重写此方法。
反射的实现原理
可以在加载程序运行时动态获取和加载程序集,并且可以获取到程序集的信息反射,是在运行期动态获取类,方法,对象,对象数据的一种手段
主要使用的类库:System.Reflection
核心类:
Assembly描述了程序集
Type描述了类这种类型
ConstructorInfo描述了构造函数
MethodInfo描述了所有的方法
FieldInfo描述了类的字段
PropertyInfo描述类的属性
在类的构造函数前加上static会报什么错?为什么?
C# 中有静态构造函数,使用 static 修饰符定义,用于初始化类的静态成员。静态构造函数在类被加载到内存中时执行,且只执行一次,而不是在创建类的实例时执行。因此,静态构造函数与常规构造函数的目的和用法是不同的。
静态成员(变量和方法)属于类本身,而不是类的实例。
可以通过类名直接访问静态成员,无需创建对象。
C#中常见的容器类
Stack:先进后出,入栈和出栈,底层泛型数组实现,入栈动态扩容二倍
Queue:先进先出,入队和出队,底层泛型数组实现,表头表尾指针,判空通过size比较
Array数组:需要声明长度,不安全
ArrayList数组列表:动态增加数组,不安全,实现了IList接口,Object数组实现
List列表:底层是泛型数组,动态扩容(将泛型数据存储在一个泛型数组中,添加元素时若超过当前泛型数组容量则二倍扩容,实现List大小动态可变
LinkList链表:底层由链表实现,数组插入删除效率高,且不用动态扩容数组
C#中常规容器和泛型容器有什么区别
不带泛型的容器需要装箱拆箱操作速度慢。泛型容器更高效更安全
常见的数值类
简单值类型:int类型,bool类型,char类型
复合值类型:结构类型,枚举类型
C#中委托 和 接口有什么区别?各用在什么场合?
委托用于表示对一个或多个方法的引用,使得可以将方法作为参数传递或进行回调操作。它常用于事件处理和异步编程,以便在特定条件下调用指定的方法。更侧重于方法的调用和执行,适用于事件和回调场景。
接口则关注类的行为和功能定义,适用于需要实现契约、增强系统灵活性和可维护性的情况。类通过实现接口来遵循一定的契约,这种设计促进了代码的解耦合和可扩展性。接口支持多重继承,可以让不同的类以相同的方式响应某些操作,从而实现多态。
C#中unsafe关键字是用来做什么的?什么场合下使用?
非托管代码才需要这个关键字,一般用在带指针操作的场合。
项目背包系统的任务装备栏使用到
C#中ref和out关键字有什么区别?
ref修饰引用参数。参数必须赋值,带回返回值,又进又出
out修饰输出参数。参数可以不赋值,带回返回值之前必须明确赋值,
引用参数和输出参数不会创建新的存储位置
如果ref参数是值类型,原先的值类型数据,会随着方法里的数据改变而改变,
如果ref参数值引用类型,方法里重新赋值后,原对象堆中数据会改变,如果对引用类型再次创建新对象并赋值给ref参数,引用地址会重新指向新对象堆数据。方法结束后形参和新对象都会消失。实参还是指向原始对象,值不够数据改变了
For,foreach,Enumerator.MoveNext的使用,与内存消耗情况
for循环可以通过索引依次进行遍历,foreach和Enumerator.MoveNext通过迭代的方式进行遍历。
内存消耗上本质上并没有太大的区别。
但是在Unity中的Update中,一般不推荐使用foreach 因为会遗留内存垃圾。
函数中多次使用string的+=处理,会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决。
通过StringBuilder那进行append,这样可以减少内存垃圾
当需要频繁创建使用某个对象时,有什么好的程序设计方案来节省内存?
设计单例模式进行创建对象或者使用对象池
Foreach循环迭代时,若把其中的某个元素删除,程序报错,怎么找到那个元素?以及具体怎么处理这种情况?(注:Try…Catch捕捉异常,发送信息不可行)
foreach不能进行元素的删除,因为迭代器会锁定迭代的集合,解决方法:记录找到索引或者key值,迭代结束后再进行删除。
GameObject a=new GameObject() GameObject b=a 实例化出来了A,将A赋给B,现在将B删除,问A还存在吗?
存在,b删除只是将它在栈中的内存删除,而A对象本身是在堆中,所以A还存在
C#中委托和事件的区别
委托就是用来存储方法的引用,让你可以方便地调用这些方法。你可以把一个方法“放进”委托中,并在需要的时候通过这个委托来“呼叫”它。这使得代码更加灵活,因为你可以根据需要选择不同的方法来执行。
事件就是一种机制,用来让对象之间互相通知。当某个事情发生了(比如一个按钮被点击),你可以触发这个事件,已注册的处理程序都会收到通知,并可以做出相应的反应。
- 触发委托有两种方式:委托实例Invoke(参数列表),委托实例(参数列表)
- 通过+=为事件注册多个委托实例或多个方法
- 通过-=为事件注销多个委托实例或多个方法
- EventHandler就是一个委托
结构体和类有何区别
结构体是一种值类型,而类是引用类型
结构体是当成值来使用的,类则通过引用对实际数据操作
C#的委托是什么?有何用处
委托类似于一种安全的指针引用,在使用它时时当作类看待而不是一个方法,相当于对一组方法的列表的引用
用处:使用委托使程序员可以将方法引用封装在委托对象内,然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道调用哪个方法。与C或C++中的函数指针不同,委托使面向对象,而且是类型安全的
foreach迭代器遍历和for循环遍历的区别
foreach:只能通过集合的迭代器直接访问元素,不能直接使用索引。这意味着你无法在循环中修改集合的结构(例如,添加或删除元素),否则将抛出异常。
for:可以通过索引直接访问元素,可以灵活地使用索引进行操作。如果需要在遍历期间对集合进行修改,通常更容易实现。
foreach遍历原理
任何集合类(Array)对象都有一个GetEnumerator()方法,该方法可以返回一个实现了 IEnumerator接口的对象。
这个返回的IEnumerator对象既不是集合类对象,也不是集合的元素类对象,它是一个独立的类对象。
通过这个实现了 IEnumerator接口对象A,可以遍历访问集合类对象中的每一个元素对象
对象A访问MoveNext方法,方法为真,就可以访问Current方法,读取到集合的元素。
C#引用和C++指针的区别
C#不支持指针,但可以使用Unsafe,不安全模式,CLR不检测
C#可以定义指针的类型、整数型、实数型、struct结构体
C#指针操作符、C#指针定义 使用fixed,可以操作类中的值类型
相同点:都是地址
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
指针是个实体,引用是个别名。
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
引用是类型安全的,而指针在不安全模式下
堆和栈的区别
栈:用于存储局部变量和方法调用信息。当一个方法被调用时,相关的局部变量、参数和返回地址等信息会被压入栈中;当方法执行结束,这些信息会被弹出。
内存的分配和释放非常快速且高效,因为它只需要移动栈指针。栈中的内存是自动管理的,当方法结束时,栈空间会自动释放。
存储在栈上的变量具有块作用域,一旦超出其作用域或方法结束,变量就会被销毁。
主要存放值类型及方法调用的信息
堆:堆是一种用于动态分配内存的数据区域,通常用于存储对象和数据结构。通过 new 关键字创建的对象都会在堆上分配内存,这些对象的生命周期由垃圾回收器(GC)管理。
内存的分配和释放相对较慢,因为它需要查找空闲块并管理复杂的内存碎片。堆中的内存由程序员通过 new 分配,并由垃圾回收器负责最终释放。
存储在堆上的对象的生命周期不受作用域限制,只要有引用指向该对象,它就会继续存在,直到没有引用时才会被垃圾回收器回收。
主要用于存放引用类型以及动态分配的对象
什么是装箱拆箱,怎样减少操作
C#装箱是将值类型转换为引用类型;
拆箱是将引用类型转换为值类型。
牵扯到装箱和拆箱操作比较多的就是在集合中,例如:ArrayList或者HashTable之类。
MVC
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。
用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
字典Dictionary的内部实现原理
Dictionary 是基于哈希表实现的,哈希表使用哈希函数将键(key)映射到数组中的存储桶(bucket)中。在 Dictionary 中,每个键值对由一个键和相应的值组成。
如果多个键的哈希值冲突(即它们被映射到同一个桶),则这些键值对会以链表的形式存储在该桶内。这种冲突解决方法称为链式散列。
通常用于快速查找和管理映射关系,适合需要快速访问的数据存储场景。
C#中的泛型是什么
在定义泛型类或方法时,可以使用一个或多个类型参数来代替实际的数据类型。
泛型可以减少装箱和拆箱的开销,因为它们允许值类型被直接存储,而无需转换为对象。可以提高性能,减少不必要的类型转换,同时保持代码的灵活性和可维护性。
C# 中的泛型提供了创建可重用、类型安全的代码的能力。
泛型可以用于类、方法和接口,并且支持多种类型约束。
反射的实现原理?
可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段
主要使用的类库:System.Reflection
核心类:
- Assembly描述了程序集
- Type描述了类这种类型
- ConstructorInfo描述了构造函数
- MethodInfo描述了所有的方法
- FieldInfo描述了类的字段
- PropertyInfo描述类的属性
通过以上核心类可在运行时动态获取程序集中的类,并执行类构造产生类对象,动态获取对象的字段或属性值,更可以动态执行类方法和实例方法等。
C#中四种访问修饰符是哪些?各有什么区别?
属性修饰符
存取修饰符
类修饰符
成员修饰符