2022年Unity 面试题 五萬字 二佰道 Unity面试题大全,面试题总结全网最全,收藏一篇足够面试

Posted 万里长江雪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022年Unity 面试题 五萬字 二佰道 Unity面试题大全,面试题总结全网最全,收藏一篇足够面试相关的知识,希望对你有一定的参考价值。

目录


??前言

  • 正所谓 金三银四 ,又到了找工作的大好时机了,不知道大家有没有意向找一份更好的工作呢~
  • 之前写了很多Unity的学习和实例文章,但是面试题部分还没有一个系统的整理。
  • 那本篇文章就来整理一下Unity中一些常见的面试题,说不准就会面试的时候就会遇到!
  • 本篇文章会将Unity所有方面的面试资料都融会贯通,绝对是2022年Unity面试领域最实用的文章啦!
  • 为了方便大家可以重点复习某个方面,所以大致分为下面几个模块方便学习
  • 看完记得 点赞 + 收藏 一波哦,过了这村可就找不到这家店了!
  • 励志做2022Unity最全的面试题整理!

本文将整理的面试题大致分为以下几个模块,方便针对性学习和背题!
由于大部分常用的面试题在网上基本上已经有比较标准的答案了,所以说面试题类的文章基本上大同小异。
所以本篇文章中的部分内容也是直接从网上摘选来的
如果有不对的地方也欢迎指正(尽力不会出现这种情况),某个模块的内容不够也欢迎在评论区指出,我去重新添加上。

  • C#基础
  • 计算机基础
  • Unity基础
  • 物理系统
  • UI部分 & 2D
  • 协程
  • 动画系统
  • 数据持久化 & 资源管理
  • Lua语言和Xlua热更
  • 网络
  • 渲染 & Shader
  • 优化部分
  • 算法

本文部分内容参考文章如下:
https://blog.csdn.net/u014361280/article/details/100135129
https://blog.csdn.net/u014361280/article/details/121499143
https://blog.csdn.net/u014361280/article/details/121534764
https://blog.csdn.net/qq_21407523/article/details/108814300


??Unity面试题大全

C#基础

1. 重载和重写的区别

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

2.面向对象的三大特点

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

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

值类型:包含了所有简单类型(整数、浮点、bool、char)、struct、enum。
继承自System.ValueTyoe
引用类型包含了string,object,class,interface,delegate,array
继承自System.Object

  1. 值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址。
  2. 值类型存取快,引用类型存取慢。
  3. 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针和引用。
  4. 栈的内存是自动释放的,堆内存是.NET 中会由 GC 来自动释放。
  5. 值类型继承自 System.ValueType,引用类型继承自 System.Object。
  6. 值类型的变直接存放实际的数据,引类型的
    变存放的则是数据的地址,即对象的引。
  7. 值类型变量直接把变量的值保存在堆栈中,引类
    型的变量把实际数据的地址保存在堆栈中。

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

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

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为了避免内存溢出而产生的回收机制

避免:
1)减少 new 产生对象的次数
2)使用公用的对象(静态成员)
3)将 String 换为 StringBuilder

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

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

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

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

10. 反射的实现原理?

可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段

主要使用的类库:System.Reflection

核心类:

  1. Assembly描述了程序集
  2. Type描述了类这种类型
  3. ConstructorInfo描述了构造函数
  4. MethodInfo描述了所有的方法
  5. FieldInfo描述了类的字段
  6. PropertyInfo描述类的属性

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

11. .Net与 Mono 的关系?

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

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

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

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

如果是处理字符串的话,用string中的方法每次都需要创建一个新的字符串对象并且分配新的内存地址,而 stringBuilder 是在原来的内存里对字符串进行修改,所以在字符串处理

方面还是建议用stringBuilder这样比较节约内存。但是 string 类的方法和功能仍然还是比 stringBuilder 类要强。

string类由于具有不可变性(即对一个 string 对象进行任何更改时,其实都是创建另外一个 string 类的对象),所以当需要频繁的对一个 string 类对象进行更改的时候,建议使用StringBuilder 类,StringBuilder 类的原理是首先在内存中开辟一定大小的内存空间,当对此 StringBuilder 类对象进行更改时, 如果内存空间大小不够, 会对此内存空间进行扩充,而不是重新创建一个对象,这样如果对一个字符串对象进行频繁操作的时候,不会造成过多的内存浪费,其实本质上并没有很大区别,都是用来存储和操作字符串的,唯一的区别就在于性能上。

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

StringBuilder主要用于拼接 String,修改性能好。

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

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

StringBuilder可变,非线程同步。

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

(a,b) => ;

15. 数列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);
       
  

16. 冒泡排序(手写代码)

public static void BubblingSort(int[]array) 

      for (int i = 0; i < array.Length; i++)
      
          for (int j = array.Length - 1; j > 0; j--)
          
              if (array[j] < array[i]) 
              
                  int temp = array[j];
                  array[j] = array[j-1];
                  array[j - 1] = temp;
              
          
      
  

17. 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
    装填因子:α=n/m=0.72 ,存储的数据N和空间大小M
    然后通过哈希桶算法,HashCode分段,每一段都是一个桶结构,一般是HashCode直接取余。
    桶结构会加剧冲突,解决冲突使用拉链法,将产生冲突的元素建立一个单链表,并将头指针地址存储至Hash表对应桶的位置。这样定位到Hash表桶的位置后可通过遍历单链表的形式来查找元素。
    1、Key—Value形式存取,无序,类型Object,需要类型转换。
    2、Hashtable查询速度快,而添加速度相对慢
    3、Hashtable中的数据实际存储在内部的一个数据桶里(bucket结构体数组),容量固定,根据数组索引获取值。

    //哈希表结构体
    private struct bucket
    public Object key;//键
    public Object val;//值
    public int hash_col;//哈希码

    //字典结构体
    private struct Entry
    public int hashCode; // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
    public int next; // 下一个元素的下标索引,如果没有下一个就为-1
    public TKey key; // 存放元素的键
    public TValue value; // 存放元素的值

    private int[] buckets; // Hash桶
    private Entry[] entries; // Entry数组,存放元素
    private int count; // 当前entries的index位置
    private int version; // 当前版本,防止迭代过程中集合被更改
    private int freeList; // 被删除Entry在entries中的下标index,这个位置是空闲的
    private int freeCount; // 有多少个被删除的Entry,有多少个空闲的位置
    private IEqualityComparer comparer; // 比较器
    private KeyCollection keys; // 存放Key的集合
    private ValueCollection values; // 存放Value的集合

性能排序:

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

18. C#中常规容器和泛型容器有什么区别,哪种效率高?

不带泛型的容器需要装箱和拆箱操作速度慢所以泛型容器效率更高数据类型更安全

19. 有哪些常见的数值类?

简单值类型:包括 整数类型、实数类型、字符类型、布尔类型

复合值类型:包括 结构类型、枚举类型

20. C#中委托 和 接口有什么区别?各用在什么场合?

**接口(interface)**是约束类应该具备的功能集合,约束了类应该具备的功能,使类从千变万化的具体逻辑中解脱出来,便于类的管理和扩展,同时又合理解决了类的单继承问题。

C#中的委托 是约束方法集合的一个类,可以便捷的使用委托对这个方法集合进行操作。

在以下情况中使用接口:

1.无法使用继承的场合
2.完全抽象的场合
3.多人协作的场合

以上等等

在以下情况中使用委托:多用于事件处理中

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

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

22. C#中ref和out关键字有什么区别?

ref修饰引用参数。参数必须赋值,带回返回值,又进又出
out修饰输出参数。参数可以不赋值,带回返回值之前必须明确赋值,
引用参数和输出参数不会创建新的存储位置

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

23. For,foreach,Enumerator.MoveNext的使用,与内存消耗情况

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

24. 函数中多次使用string的+=处理,会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决。

通过StringBuilder那进行append,这样可以减少内存垃圾

25. 当需要频繁创建使用某个对象时,有什么好的程序设计方案来节省内存?

设计单例模式进行创建对象或者使用对象池

26. JIT和AOT区别

Just-In-Time -实时编译

执行慢安装快占空间小一点

Ahead-Of-Time -预先编译

执行快安装慢占内存占外存大

27. 给定一个存放参数的数组,重新排列数组

void SortArray(Array arr)Array.Sort(arr);

28. Foreach循环迭代时,若把其中的某个元素删除,程序报错,怎么找到那个元素?以及具体怎么处理这种情况?(注:Try…Catch捕捉异常,发送信息不可行)

foreach不能进行元素的删除,因为迭代器会锁定迭代的集合,解决方法:记录找到索引或者key值,迭代结束后再进行删除。

29. GameObject a=new GameObject() GameObject b=a 实例化出来了A,将A赋给B,现在将B删除,问A还存在吗?

存在,b删除只是将它在栈中的内存删除,而A对象本身是在堆中,所以A还存在

30. C#中 委托和事件的区别

大致来说,委托是一个类,该类内部维护着一个字段,指向一个方法。事件可以被看作一个委托类型的变量,通过事件注册、取消多个委托或方法。

○ 委托就是一个类,也可以实例化,通过委托的构造函数来把方法赋值给委托实例
○ 触发委托有2种方式: 委托实例.Invoke(参数列表),委托实例(参数列表)
○ 事件可以看作是一个委托类型的变量
○ 通过+=为事件注册多个委托实例或多个方法
○ 通过-=为事件注销多个委托实例或多个方法
○ EventHandler就是一个委托

31. 结构体和类有何区别

结构体是一种值类型,而类是引用类型。(值类型、引用类型是根据数据存储的度来分的)就是值类型用于存储数据的值,引用类型用于存储对实际数据的引用。

那么结构体就是当成值来使用的,类则通过引用来对实际数据操作

32. C#的委托是什么有何用处

委托类似于一种安全的指针引用,在使用它时是 当做类来看待而不是一个方法,相当于对一组方 法的列表的引用。

用处:使用委托使程序员可以将方法引用封装在 委托对象内。然后可以将该委托对象传递给可调 用所引用方法的代码,而不必在编译时知道将调 用哪个方法。与C或C++中的函数指针不同,委托 是面向对象,而且是类型安全的。

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

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

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

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

	    List<string> list = new List<string>()  "25", "哈3", "26", "花朵" ;
 		IEnumerator listEnumerator = list.GetEnumerator();
        while (listEnumerator.MoveNext())
        
            Console.WriteLine(listEnumerator.Current);
        

34. C#和C++的区别

简单的说:C# 与C++ 比较的话,最重要的特性 就是C# 是一种完全面向对象的语言,而C++ 不 是,另外C# 是基于IL 中间语言
和.NET Framework CLR 的,在可移植性,可维 护性和强壮性都比C++ 有很大的改进。C# 的设 计目标是用来开发快速稳定可扩展的应用程序, 当然也可以通过Interop和Pinvoke 完成一些底层操作

具体对比

  1. 继承:C++支持多继承,C#类只能继承一个基类中的实现但可以实现多个接口。
  2. 数组:声明 C# 数组和声明 C++ 数组的语法不同。在 C# 中,“[]”标记出现在数组类型的后面。
  3. 数据类型:在C++中bool类可以与整型转换,但C#中bool 类型和其他类型(特别是 int)之间没有转换。long 类型:在 C# 中,long 数据类型为 64 位,而在 C++ 中为 32 位。
  4. struct 类型:在 C# 中,类和结构在语义上不同。struct 是值类型,而 class 是引用类型。
  5. switch 语句:与 C++ 中的 switch 语句不同,C# 不支持从一个 case 标签贯穿到另一个 case 标签。
  6. delegate 类型:委托与 C++ 中的函数指针基本相似,但前者具有类型安全,是安全的。
  7. 从派生类调用重写基类成员。 base
  8. 使用 new 修饰符显式隐藏继承成员。
  9. 重写方法需要父类方法中用virtual声名,子类方法用override 关键字。
  10. 预处理器指令用于条件编译。C# 中不使用头文件。 C# 预处理器指令
  11. 异常处理:C#中引入了 finally 语句,这是C++没有的。
  12. C# 运算符:C# 支持其他运算符,如 is 和 typeof。它还引入了某些逻辑运算符的不同功能。
  13. static 的使用,static方法只能由类名调用,改变static变量。
  14. 在构造基类上替代 C++ 初始化列表的方法。
  15. Main 方法和 C++ 及Java中的 main 函数的声明方式不同,Main而不能用main
  16. 方法参数:C# 支持 ref 和 out 参数,这两个参数取代指针通过引用传递参数。
  17. 在 C# 中只能在unsafe不安全模式下才使用指针。
  18. 在 C# 中以不同的方式执行重载运算符。
  19. 字符串:C# 字符串不同于 C++ 字符串。
  20. foreach:C#從VB中引入了foreach关键字使得以循环访问数组和集合。
  21. C# 中没有全局方法和全局变量:方法和变量必须包含在类型声明(如 class 或 struct)中。
  22. C# 中没有头文件和 #include 指令:using 指令用于引用其他未完全限定类型名的命名空间中的类型。
  23. C# 中的局部变量在初始化前不能使用。
  24. 析构函数:在 C# 中,不能控制析构函数的调用时间,原因是析构函数由垃圾回收器自动调用。 析构函数
  25. 构造函数:与 C++ 类似,如果在 C# 中没有提供类构造函数,则为您自动生成默认构造函数。该默认构造函数将所有字段初始化为它们的默认值。
  26. 在 C# 中,方法参数不能有默认值。如果要获得同样的效果,需使用方法重载。

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

C#不支持指针,但可以使用Unsafe,不安全模式,CLR不检测
C#可以定义指针的类型、整数型、实数型、struct结构体
C#指针操作符、C#指针定义
使用fixed,可以操作类中的值类型
相同点:都是地址
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。

不同点
指针是个实体,引用是个别名。
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
引用是类型安全的,而指针在不安全模式下

36. 堆和栈的区别

通常保存着我们代码执行的步骤,如在代码段1中 AddFive()方法,int pValue变量,int result变量等等。 而堆上存放的则多是对象,数据等。(译者注:忽略编 译器优化)我们可以把栈想象成一个接着一个叠放在 一起的盒子。当我们使用的时候,每次从最顶部取走 一个盒子。栈也是如此,当一个方法(或类型)被调 用完成的时候,就从栈顶取走(called a Frame,译 注:调用帧),接着下一个。堆则不然,像是一个仓 库,储存着我们使用的各种对象等信息,跟栈不同的 是他们被调用完毕不会立即被清理掉。

37. Heap与Stack有何区别

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

38. Mock和Stub有何区别

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

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

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

40. 简述StringBuilder和String的区别?(字符串处理)

String是字符串常量。StringBuffer是字符串变量 ,线程安全。StringBuilder是字符串变量,线程不安全。

String类型是个不可变的对象,当每次对String进改变时都需要成个新的String对象,然后将指针指向个新的对象,如果在个循环,不断的改变个对象,就要不断的成新的对象,所以效率很低,建议在不断更改String对象的地不要使String类型。

StringBuilder对象在做字符串连接操作时是在原来的字符串上进修改,改善了性能。这点我们平时使中也许都知道,连接操作频繁的时候,使StringBuilder对象。

41. 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

42. 字典Dictionary的内部实现原理

泛型集合命名空间using System.Collections.Generic;
任何键都必须是唯一

该类最大的优点就是它查找元素的时间复杂度接近O(1),实际项目中常被用来做一些数据的本地缓存,提升整体效率。

实现原理

  1. 哈希算法:将不定长度的二进制数据集给映射到一个较短的二进制长度数据集一个Key通过HashFunc得到HashCode
  2. Hash桶算法:对HashCode进行分段显示,常用方法是对HashCode直接取余
  3. 解决碰撞冲突算法(拉链法):分段会导致key对应的桶会相同,拉链法的思想就像对冲突的元素,建立一个单链表,头指针存储到对应的哈希桶位置。反之就是通过确定hash桶位置后,遍历单链表,获取对应的value

43. 泛型是什么

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

泛型类不是实际的类,而是类的模板
从泛型类型创建实例
声明泛型类型》通过提供【真实类型】创建构造函数类型》从构造类型创建实例
类<T1,T2> 泛型类型参数

性能:泛型不会强行对值类型进行装箱和拆箱,或对引用类型进行向下强制类型转换,所以性能得到提高

安全:通过知道使用泛型定义的变量的类型限制,编译器可以在一定程度上验证类型假设,所以泛型提高了程序的类型安全。

44. Mathf.Round和Mathf.Clamp和Mathf.Lerp含义?

  • Mathf.Round:四舍五入
  • Mathf.Clamp:左右限值
  • Mathf.Lerp:插值

45. 能用foreach遍历访问的对象需要实现______接或声明_________法的类型(C#遍历)

IEnumerable;GetEnumerator

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

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

里氏替换原则(Liskov Substitution Principle LSP)向对象设计的基本原则之。

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

47. 反射的实现原理?

可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段

主要使用的类库:System.Reflection

核心类:

  1. Assembly描述了程序集
  2. Type描述了类这种类型
  3. ConstructorInfo描述了构造函数
  4. MethodInfo描述了所有的方法
  5. FieldInfo描述了类的字段
  6. PropertyInfo描述类的属性

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

审查元数据并收集关于它的类型信息的能。

实现步骤:
1. 导?using System.Reflection;
2. Assembly.Load("程序集")加载程序集,返回类型是
?个Assembly
3.  foreach (Type type in assembly.GetTypes())
         
            string t = type.Name;
         
得到程序集中所有类的名称
4. Type type = assembly.GetType("程序集.类名");获取
当前类的类型
5. Activator.CreateInstance(type); 创建此类型实例
6. MethodInfo mInfo = type.GetMethod("?法名");获取
当前?法
7. mInfo.Invoke(null,?法参数);

48. 概述c#中代理和事件?

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

49. 哈希表与字典对比

字典:内部用了Hashtable作为存储结构

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

哈希表

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

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

  1. 属性修饰符
  2. 存取修饰符
  3. 类修饰符
  4. 成员修饰符

属性修饰符:
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 修饰符修饰它。

51. 下列代码在运行中会发生什么问题?如何避免?

List<int> ls = new List<int>(new int[] 1, 2, 3, 4, 5 );
         foreach (int item in ls)
         
             Console.WriteLine(item * item);
             ls.Remove(item);
         

会产运时错误,因为foreach是只读的。不能边遍历边修改。

使用For循环遍历可以解决。

52. 什么是装箱拆箱,怎样减少操作

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

以上是关于2022年Unity 面试题 五萬字 二佰道 Unity面试题大全,面试题总结全网最全,收藏一篇足够面试的主要内容,如果未能解决你的问题,请参考以下文章

2022年Unity 面试题 |五萬字 二佰道| Unity面试题大全,面试题总结全网最全,收藏一篇足够面试

[Unity面试] 2021年Unity面试题分享(面试题Lua突破3.8已更新)

2022年Unity客户端面试题总结

2022年Java面试题最新整理,附白话答案

Android面试题及答案整理(2022年最新Android面试题大全带答案)

2022年Java秋招面试,程序员求职必看的Dubbo面试题