1.面向对象的三大特点
继承: 提高代码重用度,增强软件可维护性的重要手段,符合开闭原则。继承最主要的作用就是把子类的公共属性集合起来,便与共同管理,使用起来也更加方便。你既然使用了继承,那代表着你认同子类都有一些共同的特性,所以你把这些共同的特性提取出来设置为父类。继承的传递性:传递机制 a▶b; b▶c; c具有a的特性 。继承的单根性:在C#中一个类只能继承一个类,不能有多个父类。
封装: 封装是将数据和行为相结合,通过行为约束代码修改数据的程度,增强数据的安全性,属性是C#封装实现的最好体现。就是将一些复杂的逻辑经过包装之后给别人使用就很方便,别人不需要了解里面是如何实现的,只要传入所需要的参数就可以得到想要的结果。封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
多态性: 多态性是指同名的方法在不同环境下,自适应的反应出不同得表现,是方法动态展示的重要手段。多态就是一个对象多种状态,子类对象可以赋值给父类型的变量。
2. 简述值类型和引用类型
介绍
值类型:包含了所有简单类型(整数、浮点、bool、char)、struct、enum。
继承自System.ValueTyoe
引用类型包含了string,object,class,interface,delegate,array
继承自System.Object
区别
值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址。
值类型存取快,引用类型存取慢。
值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针和引用。
栈的内存是自动释放的,堆内存是.NET 中会由 GC 来自动释放。
值类型继承自 System.ValueType,引用类型继承自 System.Object。
值类型在栈中存储的是直接的值,引用类型数据本身实在堆中,栈中存放的是一个引用的地址。
3. 重载和重写的区别
封装、继承、多态所处位置不同,重载在同类中,重写在父子类中。
定义方式不同,重载方法名相同参数列表不同,重写方法名和参数列表都相同。
调用方式不同,重载使用相同对象以不同参数调用,重写用不同对象以相同参数调用。
多态时机不同,重载时编译时多态,重写是运行时多态。
4. Net与 Mono 的关系?
.Net是一个语言平台,Mono为.Net提供集成开发环境,集成并实现了.NET的编译器、CLR 和基础类库,使得.Net既可以运行在windows也可以运行于 linux,Unix,Mac OS 等。
5. C#中所有引用类型的基类是什么
引用类型的基类是System.Object值类型的基类是 System.ValueType。
同时,值类型也隐式继承自System.Object。
6. 请简述ArrayList和 List的主要区别
ArrayList 不带泛型 数据类型丢失
List 带泛型 数据类型不丢失
ArrayList 需要装箱拆箱 List不需要
ArrayList存在不安全类型(ArrayList会把所有插 ⼊其中的数据都当做Object来处理)装箱拆箱的 操作(费时)IList是接⼝,ArrayList是⼀个实现了 该接⼝的类,可以被实例化
List类是ArrayList类的泛型等效类。它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。
7. GC 相关知识点
GC的概念
C#内部有两个内存管理池:堆内存和栈内存。栈内存(stack)主要用来存储较小的和短暂的数据,堆内存(heap)主要用来存储较大的和存储时间较长的数据。C#中的变量只会在堆栈或者堆内存上进行内存分配,变量要么存储在栈内存上,要么处于堆内存上。
只要变量处于激活状态,则其占用的内存会被标记为使用状态,则该部分的内存处于被分配的状态。
一旦变量不再激活,则其所占用的内存不再需要,该部分内存可以被回收到内存池中被再次使用,这样的操作就是内存回收。处于栈上的内存回收及其快速,处于堆上的内存并不是及时回收的,此时其对应的内存依然会被标记为使用状态。不再使用的内存只会在GC的时候才会被回收。
垃圾回收主要是指堆上的内存分配和回收,C#中会定时对堆内存进行GC操作。
GC会带来的问题
游戏性能:GC操作是一个极其耗费事件的操作,堆内存上的变量或者引用越多则导致遍历检查时的操作变得十分缓慢,使得游戏运行缓慢,例如当CPU处于游戏性能的关键时刻,任何一个操作就会导致游戏帧率下降,造成极大的影响。
游戏内存:(unityGC采用的是非分代非压缩的标记清除算法)GC操作会产生“内存碎片化”。当一个单元内存从堆中分配出来,其大小取决于存储变量的大小。当内存被回收到堆上时,有可能被堆内存分割成碎片化的单元。(就是说总容量大小时固定的,但是单元内存较小。例如房子很大,房间很小,找不到合适的房间)即下次分配时找不到合适的储存单元,就会触发GC操作,或者堆内存扩容操作,导致GC频发发生和游戏内存越来越大。
GC触发时机
在堆内存上进行内存分配操作,而内存不够的时候都会触发垃圾回收来利用闲置的内存;
GC会自动的触发,不同平台运行频率不—样;
GC可以被强制执行。
如何避免GC?
减少临时变量的使用,多使用公共对象,多利用缓存机制。(将容器定义到函数外,用到容器的时候进行修改即可)
减少new对象的次数。
对于大量字符串拼接时,将StringBuilder代替String。(string不可修改性,修改即创建一个新的string对象,旧的直接抛弃等待GC,但少量字符串拼接用string,性能优于stringbuilder)
使用扩容的容器时,例如:List,StringBuilder等,定义时尽量根据存储变量的内存大小定义储存空间,减少扩容的操作。(扩容后,旧的容器直接抛弃等待GC)
代码逻辑优化:例如计时器当大于1s后才进行文本修改,而不是每帧都修改,或者禁止在关键时候GC,影响游戏性能,可以在加载页面或者进度条的时候GC。
利用对象池:对象池是一种Unity经常用到的内存管理服务,针对经常消失生成的对象,例如子弹,怪物等,作用在于减少创建每个对象的系统开销。在我们想要对象消除时,不直接Destory,而是隐藏起来SetActive(false),放入池子中,当需要再次显示一个新的对象时,先去池子中看有没有隐藏对象,有就取出来(显示) SetActive(true),没有的话,再实例化。
减少装箱拆箱( 装箱是将值类型转换为 object 类型或由此值类型实现的任何接口类型的过程)的操作
协程: yeild return 0 会产生装箱拆箱,可以替换为 yeild return null。
8. 结构体和类
结构体和类的区别
结构体是值类型,类是引用类型。
结构体存在栈中,类存在堆中。
结构体变量和类对象进行类型传递时,结构体变量进行的就是值传递,而类对象进行的是引用传递,或者说传递的是指针,这样在函数中改变参数值,结构体对象的值是不变的,而类对象的值是变化了。
在C#中结构体类型定义时,成员是不能初始化的,这样就导致了,定义结构体变量时,变量的所有成员都要自己赋值初始化。但对于类,在定义类时,就可以初始化其中的成员变量,所以在定义对象时,对象本身就已经有了初始值,你可以自己在重新给个别变量赋值。(注意在C++中,类的定义中是不能初始化的,初始化要放在构造函数中)
结构体不能申明无参的构造函数,而类可以。
声明了结构类型后,可以使用new运算符创建构造对象,也可以不使用new关键字。如果不使用new,那么在初始化所有字段之前,字段将保持未赋值状态且对象不可用。
结构体申明有参构造函数后,无参构造不会被顶掉。
结构体不能申明析构函数,而类可以。
结构体不能被继承,而类可以。
结构体需要在构造函数中初始化所有成员变量,而类随意。
结构体不能被静态static修饰(不存在静态结构体),而类可以。
使用情景
结构体:
结构体是值类型在栈中,栈的存取速度比堆快,但是容量小,适合轻量级的对象,比如点、矩形、颜色。
如果对象是数据集合时,优先考虑接结构体(位置,坐标)
在变量传值的时候,希望传递对象的是拷贝,而不是对象的引用地址,这个时候就可以使用结构体。
类:
类是引用类型,存储在堆中,堆的容量大,适合重量级的对象,栈的空间不大,大量的对应当存在于堆中。
如果对象需要继承和多态特征,用类(玩家、怪物)。
9. C#中四种访问修饰符是哪些?各有什么区别?
属性修饰符
存取修饰符
类修饰符
成员修饰符
属性修饰符:
Serializable:按值将对象封送到远程服务器。
STATread:是单线程套间的意思,是⼀种线程模型。
MATAThread:是多线程套间的意思,也是⼀种线程模
型。
存取修饰符:
public:存取不受限制。
private:只有包含该成员的类可以存取。
internal:只有当前⼯程可以存取。
protected:只有包含该成员的类以及派⽣类可以存
取。
类修饰符:
abstract:抽象类。指示⼀个类只能作为其它类的基
类。
sealed:密封类。指示⼀个类不能被继承。理所当
然,密封类不能同时⼜是抽象类,因为抽象总是希望
被继承的。
成员修饰符:
abstract:指示该⽅法或属性没有实现。
sealed:密封⽅法。可以防⽌在派⽣类中对该⽅法的
override(᯿载)。不是类的每个成员⽅法都可以作为
密封⽅法密封⽅法,必须对基类的虚⽅法进⾏᯿载,
提供具体的实现⽅法。所以,在⽅法的声明中,
sealed修饰符总是和override修饰符同时使⽤。
delegate:委托。⽤来定义⼀个函数指针。C#中的事
件驱动是基于delegate + event的。
const:指定该成员的值只读不允许修改。
event:声明⼀个事件。
extern:指示⽅法在外部实现。
override:᯿写。对由基类继承成员的新实现。
readonly:指示⼀个域只能在声明时以及相同类的内
部被赋值。
static:指示⼀个成员属于类型本身,⽽不是属于特定
的对象。即在定义后可不经实例化,就可使⽤。
virtual:指示⼀个⽅法或存取器的实现可以在继承类中
被覆盖。
new:在派⽣类中隐藏指定的基类成员,从⽽实现᯿
写的功能。 若要隐藏继承类的成员,请使⽤相同名称
在派⽣类中声明该成员,并⽤ new 修饰符修饰它。
10. 修饰符 简述private,public,protected,internal的区别
public:对任何类和成员都公开,无限制访问
private:仅对该类公开
protected:对该类和其派生类公开
internal:只能在包含该类的程序集中访问该类
protected internal:protected + internal