Unity 面试题——C#基础

1.重载和重写的区别

类型封装继承、多态所处位置不同定义式不同调用方式多态时机
重载同类方法名相同参数列表不同使用相同对象以不同参数调用编译时多态
重写父子类方法名和参数列表都相同用不同对象以相同参数调用运行时多态

2.面向对象的三大特点

  • 封装:封装是将数据和行为相结合,行为约束代码修改数据的程度,如属性。将复杂逻辑包装之后,不需要了解里面如何实现,传入参数就可以得到想要的结果。封装的意义在于保护或者防止代码(数据) 被我们无意中破坏,增强数据的安全性。
  • 继承:继承最主要的作用就是把子类的公共属性集合起来,便于共同管理,使用方便。把子类共同的特性提取设置为父类。继承的传递性:传递机制a>b; b>c;具有a的特性。继承的单根性:不能有多个父类。提高代码重用度, 增强软件可维护性,符合开闭原则。
  • 多态性:同名的方法在不同环境下,自适应的反应出不同得表现,方法动态展示的重要手段。多态就是一个对象多种状态,子类对象可以赋值给父类型的变量。

3.简述值类型和引用类型有什么区别

类型包含存储存取继承内存释放
值类型简单类型(整数int、浮点float、boo、char)、 struct、enum内存栈,实际数据System.ValueType,隐性继承Object系统自动
引用类型string、object、class、 interface、delegate、 array内存堆,指向存储在内存堆中的数据的指针和引用System.ObjectGC

4.请简述private, public, protected, internal的区别

public对任何类和成员都公开,无限制访问
private仅对该类公开
protected对该类和其派生类公开
internal只能在包含该类的程序集中访问该类

5.请简述ArrayList和List的主要区别

  • ArrayList 不带泛型数据类型丢失,需要装箱拆箱
  • List带泛型数据类型不丢失,List不需要

ArrayList存在不安全类型(ArrayList会把所有插 入其中的数据都当做Object来处理)装箱拆箱的操作(费时) IList是接口, ArrayList是一个实现了该接口的类,可以被实例化
List类是ArrayList类的泛型等效类。它的大部分用法都与ArrayList相似, 因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。

6.请简述GC (垃圾回收)产生的原因,并描述如何避免?
GC为了避免内存溢出而产生的回收机制
如何避免:
1)减少new产生对象的次数
2)使用公用的对象(静态成员)
3)将String换为StringBuilder


7.请描述Interface与抽象类之间的不同

  • 接口不是类不能实例化抽象类可以间接实例化
  • 接口是完全抽象抽象类为部分抽象
  • 接口可以多继承抽象类是单继承


8.请简述关键字Sealed用在类声明和函数声明时的作用

  • 类声明时可防止其他类继承此类
  • 在方法中声明则可防止派生类重写此方法

9.反射的实现原理?
可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段
主要使用的类库: System.Reflection
核心类:

  • Assembly描述了程序集
  • Type描述了类这种类型
  • ConstructorInfo描述了构造函数
  • MethodInfo描述了所有的方法
  •  FieldInfo描述了类的字段
  • PropertyInfo描述类的属性

通过以上核心类可在运行时动态获取程序集中的类,并执行类构造产生类对象,动态获取对象的字段或属性值,更可以动态执行类方法和实例方法等。
 

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


11.在类的构造函数前加上static会报什么错?为什么?
构造函数格式为public+类名如果加上static会报错(静态构造函数不能有访问型的对象,静态构造函数只执行一次);
运行库创建类实例或者首次访问静态成员之前,运行库调用静态构造函数;
静态构造函数执行先于任何实例级别的构造函数;
显然也就无法使用this和base来调用构造函数。
一个类只能有一个静态函数,如果有静态变量,系统也会自动生成静态函数


12.C# String类型比stringBuilder类型的优势是什么?

  • string类由于具有不可变性,对string对象进行任何更改都是创建另外一个string类的对象,需要创建一个新的字符串对象并且分配新的内存地址
  • stringBuilder是在原来的内存里对字符串进行修改,所以在字符串处理用stringBuilder比较节约内存,频繁更改string类对象时,使用StringBuilder 类

 StringBuilder 类的原理:首先在内存中开辟一定大小的内存空间, 当对此StringBuilder类对象进行更改时,如果内存空间大小不够,会对此内存空间进行扩 充,而不是重新创建一个对象

  • String主要用于公共API,通用性好、用途广泛读取性能高、占用内存小。
  • StringBuilder主要用于拼接String,修改性能好。

不过现在的编译器已经把String的+操作优化成StringBuilder了,所以一般用String 就可以了

  • String是不可变的,所以天然线程同步。StringBuilder可变,非线程同步。

13.C#函数Func(string a, string b)用Lambda表达式怎么写?

(a,b) => {};


14.数列1,1,2,3,5,8,13...第n位数是多少?用C#递归算法实现

public int CountNumber(int num) {
    if(num==1||num==2)
        return 1;
    else 
        return CountNumber(num -1) + CountNumber(num - 2); 
}

15.冒泡排序代码

void BubbleSort(ElemType A[],int n){
    for(i=0;i<n-1;i++) 
    {
        flag= false;               //表示本趟冒泡是否发生交换的标志 优化1
        for(j=n-1;j>i;j--){        //一趟冒泡过程
            if(A[j-1]>A[j]){       //若为逆序
                swap(A[j-1],A[j]); //交换
                flag=true;
            }
        }
        if (flag==false)
        return;                    //本趟遍历后没有发生交换,说明表已经有序
    }
}

学习排序算法:十大排序算法及其优化_是否对错的博客-CSDN博客

16. C#中有哪些常用的容器类,各有什么特点。
List, HashTable, Dictionary, Stack, Queue

  • Stack栈: 先进后出,入栈和出栈,底层泛型数组实现,入栈动态扩容2倍
  • Queue队列:先进先出,入队和出队,底层泛型数组实现,表头表尾指针,判空还是满通过size比较,Queue和Stack主要是用来存储临时信息的
  • Array数组:需要声明长度,不安全
  • ArrayList数组列表: 动态增加数组,不安全,实现了IList接口(表示可按照索弓|进行访问的非泛型集合对象),Object数组实现
  • List列表: 底层泛型数组实现,特性,动态扩容,泛型安全。将泛型数据(对值类型来说就是数据本身,对引用类型来说就是引用)存储在一个泛型数组中, 添加元素时若超过当前泛型数组容量,则以2倍扩容,进而实现List大小动态可变。(注: 大小指容量,不是Count)
  • LinkList链表
  1. 数组和List、 ArrayList集合都有一 个重大的缺陷, 就是从数组的中间位置删除或插入一个元素需要付出很大的代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。
  2. LinkedList (底层是由链表实现的)基于链表的数据结构,很好的解决了数组删除插入效率低的问题,不用动态的扩充数组的长度。
  3. LinkedList的优点: 插入、删除元素效率比较高;缺点:访问效率比较低。
  • HashTable哈希表 (散列表)
    概念:不定长的二进制数据通过哈希函数映射到一个较短的二进制数据集,即Key通过HashFunction函数获得HashCode
    装填因子: a=n/m=0.72 ,存储的数据N和空间大小M
    然后通过哈希桶算法, HashCode分段,每一段都是一 个桶结构, 一般是HashCode直接取余。
    桶结构会加剧冲突,解决冲突使用拉链法,将产生冲突元素建立一个单链表, 并将头指针地址存储Hash表对应桶的位置。这样定位到Hash表桶的位置后通过遍历单链表的形式来查找元素。
    1、Key- Value形式存取,无序,类型Object, 需要类型转换。
    2、Hashtable查询速度快,而添加速度相对慢
    3、Hashtable中的数据实际存储在内部的一个数据桶里(bucket结构体数组),容量固定,根据数组索引|获取值。
  • 性能排序:
    插入性能: LinkedList > Dictionary > HashTable > List
    遍历性能: List > LinkedList > Dictionary > HashTable
    删除性能: Dictionary > LinkedList > HashTable > List

17. C#中常规容器和泛型容器有什么区别,哪种效率高?
不带泛型的容器需要装箱和拆箱操作速度慢所以泛型容器效率更高数据类型更安全

18. C#中委托和接口有什么区别?各用在什么场合?
接口(interface) 是约束类应该具备的功能集合,约束了类应该具备的功能,使类从千万化的具体逻辑中解脱出来,便于类的管理和扩展,同时又合理解决了类的单继承问题。
C#中的委托是约束方法集合的一个类,可以便捷的使用委托对这个方法集合进行操作。
使用接口:
1.无法使用继承的场合
2.完全抽象的场合
3.多人协作的场合

使用委托:事件处理

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

  • 非托管代码才需要这个关键字一般用在带指针操作的场合。
  • 项目背包系统的任务装备栏使用到

20. C#中ref和out关键字有什么区别?
ref修饰引用参数。参数必须赋值,带回返回值,又进又出
out修饰输出参数。参数可以不赋值,带回返回值之前必须明确赋值,
引用参数和输出参数不会创建新的存储位置

如果ref参数是值类型,原先的值类型数据,会随着方法里的数据改变而改变,
如果ref参数值引用类型,方法里重新赋值后,原对象堆中数据会改变,如果对引用类型再次创建新对象并赋值给ref参数,引用地址会重新指向新对象堆数据。方法结束后形参和新对象都会消失。实参还是指向原始对象,值不够数据改变了

21. For, foreach, Enumerator.MoveNext的使用, 与内存消耗情况
for循环可以通过索引|依次进行遍历,foreach和Enumerator.MoveNext通过迭代的方式进行遍历。
内存消耗上本质上并没有太大的区别。
但是在Unity中的Update中,一般不推荐使用foreach 因为会遗留内存垃圾。
 

22.当需要频繁创建使用某个对象时,有什么好的程序设计方案来节省内存?
设计单例模式进行创建对象或者使用对象池

23. JIT和AOT区别
Just-ln-Time -实时编译
执行慢安装快占空间小一点
Ahead-Of-Time -预先编译
执行快安装慢占内存占外存大

24.给定一个存放参数的数组,重新排列数组
void SortArray(Array arr){Array. Sort(arr);}

25. Foreach循环迭代时,若把其中的某个元素删除,程序报错,怎么找到那个元素?以及具体怎么处理这种情况? (注: Try..Catch捕捉异常,发送信息不可行)
foreach不能进行元素的删除,因为迭代器会锁定迭代的集合,解决方法:记录找到索引或者key值,迭代结束后再进行删除。

26. GameObject a=new GameObject() GameObject b=a实例化出来了A,将A赋给B,现在将B删除,问A还存在吗?
存在,b删除只是将它在栈中的内存删除,而A对象本身是在堆中,所以A还存在

27. C#中委托和事件的区别
大致来说,委托是一个类,该类内部维护着一个字段, 指向一个方法。事件可以被看作-个委托类型的变量, 通过事件注册、取消多个委
托或方法。
。委托就是一个类, 也可以实例化,通过委托的构造函数来把方法赋值给委托实例
。触发委托有2种方式:委托实例.Invoke(参数列表), 委托实例(参数列表)
。事件可以看作是一个委托类型的变量
。通过+=为事件注册多个委托实例或多个方法
。通过-=为事件注销多个委托实例或多个方法
。EventHandler就是一个委托

28.结构体和类有何区别?
结构体是一种值类型,而类是引用类型。(值类型、弓|用类型是根据数据存储的角度来分的)就是值类型用于存储数据的值,引用类型用于存储对实际数据的引用。
那么结构体就是当成值来使用的,类则通过引用来对实际数据操作

29. C#的委托是什么?有何用处?
委托类似于一种安全的指针引用,在使用它时是当做类来看待而不是一个方法, 相当于对一组方法的列表的引用。
用处:使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引|用方法的代码,而不必在编译时
知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的。

30. foreach迭代器遍历和for循环遍历的区别
如果集合需要foreach遍历,是否可行,存在一定问题
foreach中的迭代变量item是的只读,不能对其进行修改,比如list.Remove (item) 操作
foreach只读的时候记录下来,在对记录做操作,或者直接用for循环遍历
foreach对int数组循环已经不产生GC,避免对ArrayList进行遍历

for语句中初始化变量i的作用域,循环体内部可见。
通过索引进行遍历,可以根据索引对所遍历集合进行修改
unity中for循环使用lambda表达式注意闭包问题

foreach遍历原理
任何集合类(Array) 对象都有一个GetEnumerator()方法, 该方法可以返回一一个实现了lEnumerator接口的对象。
这个返回的IEnumerator对象既不是集合类对象,也不是集合的元素类对象,它是一个独立的类对象。
通过这个实现了lEnumerator接口对象A,可以遍历访问集合类对象中的每一个元素对象
对象A访问MoveNext方法,方法为真,就可以访问Current方法,读取到集合的元素。

31. Heap与Stack有何区别?
1. heap是堆, stack是栈。
2. stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。
3. stack空间有限,heap 的空间是很大的自由区。

32.为什么dynamic font在unicode环境下优于staticfont (字符串编码)
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。
使用动态字体时,Unity将不会预先生成一个与所有字体的字符纹理。
当需要支持亚洲语言或者较大的字体的时候,若使用正常纹理,则字体的纹理将非常大。

 33. string. stringBuilder. stringBuffer
●String不变性,字符序列不可变,对原管理中实例对象赋值,会重新开一个新的实例对象赋值, 新开的实例对象会等待被GC。
string拼接要重新开辟空间,因为string原值不会改变,导致GC频繁,性能消耗大
●StringBuffer是字符串可变对象,可通过自带的StringBuffer.方法来改变并生成想要的字符串。对原实例对象做拼接的实例,不会生成新的实例对象。
拼接使用StringBuilder和StringBuffer,只开辟一个内存空间, 这是性能优化的点。
●StringBuilder是字符串可变对象,基本和StringBuilder相同。
唯一的区别是StringBuffer是线程安全,相关方法前带synchronized关键字,一般用于多线程
StringBuilder是非线程安全,所以性能略好,- 般用于单线程
三者性能比较StringBuilder>StringBuffer> String
1.如果要操作少量的数据=string
2.单线程操作字符串缓冲区下操作大量数据= StringBuilder
3.多线程操作字符串缓冲区下操作大量数据= StringBuffer

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

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

泛型类不是实际的类,而是类的模板
从泛型类型创建实例

声明泛型类型》通过提供[真实类型]创建构造函数类型》从构造类型创建实例
类<T1,T2>泛型类型参数
性能:泛型不会强行对值类型进行装箱和拆箱,或对引用类型进行向下强制类型转换,所以性能得到提高
安全:通过知道使用泛型定义的变量的类型限制,编译器可以在一定程度上验证类型假设,所以泛型提高了程序的类型安全。
 

36. Mathf.Round和Mathf.Clamp和Mathf.Lerp含义?
●Mathf.Round: 四舍五入
●Mathf.Clamp: 左右限值
●Mathf.Lerp: 插值

37.能用foreach遍历访问的对象需要实现____接口或声明__方法的类型(C#遍历)
lEnumerable; GetEnumerator
List和Dictionary类型可以用foreach遍历,他们都实现了lEnumerable接口,申明了GetEnumerator方法。

38.概述c#中代理和事件?
代理就是用来定义指向方法的引用。
C #事件本质就是对消息的封装,用作对象之间的通信;发送方叫事件发送器,接收方叫事件接收器; 

39.哈希表与字典对比
字典:内部用了Hashtable作为存储结构
●如果我们试图找到一个不存在的键,它将返回/抛出异常。
●它比哈希表更快,因为没有装箱和拆箱,尤其是值类型。
●仅公共静态成员是线程安全的。
●字典是一种通用类型,这意味着我们可以将其与任何数据类型一起使用(创建时,必须同时指定键和值的数据类型)。
●Dictionay是Hashtable的类型安全实现, Keys和Values是强类型的。
●Dictionary遍历输出的顺序,就是加入的顺序

哈希表: 
●如果我们尝试查找不存在的键,则返回null。
●它比字典慢,因为它需要装箱和拆箱。
●哈希表中的所有成员都是线程安全的,
●哈希表不是通用类型,Hashtable是松散类型的数据结构,我们可以添加任何类型的键和值。
●HashTable是经过优化的,访问下标的对象先散列过,所以内部是无序散列的
 

40. C#中四种访问修饰符是哪些?各有什么区别?
1.属性修饰符
2.存取修饰符
3.类修饰符
4.成员修饰符

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

存取修饰符:
public:存取不受限制。
private:只有包含该成员的类可以存取。

protected:只有包含该成员的类以及派生类可以存取。
internal:只有当前工程可以存取。

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

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

41.什么是装箱拆箱,怎样减少操作
C#装箱是将值类型转换为引用类型;拆箱是将引用类型转换为值类型。
牵扯到装箱和拆箱操作比较多的就是在集合中,例如: ArrayList或者HashTable之类。

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值