Csharp

1.c#gc的触发条件有哪些,如何避免频繁gc,如何手动控制gc

触发条件:

  • 对象的物理内存较低

  • 托管堆上分配的对象占用的内存超出了可接受阈值

  • 调用 GC.Collect() 方法

如何避免频繁 GC

  • 对于频繁出现消失的物体使用对象池管理

  • 减少 new 产生对象的次数

  • 使用公用的对象,即静态成员 Static ,但要谨慎使用(例如一个 List<int> 含有大量的数据却没有清除一直占用着内存)

  • 如果是在 unity 当中,尽量避免使用 foreach ,尽量使用 for

Unity 当中为什么要尽量避免使用 foreachforeach 内部有迭代器,会导致装箱操作,进而引起频繁的 GC.Alloc ,尤其是在 Update 当中调用其之后会造成大量的小块垃圾内存,造成垃圾回收操作的提前到来,造成游戏间歇性的卡顿。

如何手动控制 gc : 使用 GC.Collect() 方法,可以传入一个 0 , 1, 2 三个数字,以清除第几代的托管堆内存

2.什么是异步,为什么要使用异步编程,异步与多线程的关系,task和thread的区别,Unity的协程和Task的区别

什么是异步: 异步是一种编程模型,其允许程序在执行某个操作的时候不需要等待其完成,而是可以执行其他的任务。在异步编程中,程序的执行是非阻塞的。

为什么要使用异步编程: 当使用一些耗时的操作时,如 I/O,或者网络请求,此时同步运行就会阻塞掉我们的程序,导致在等待这个操作完成之前程序都无法响应外部的操作。而使用异步编程时则不会被阻塞。

异步与多线程的关系:

  • 异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。

  • 多线程是程序设计的逻辑层概念,它是进程中并发运行的一段代码。多线程可以实现线程间的切换执行。

  • 多线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。

TaskThread 的区别:

  • Task 可以有返回值,而 Thread 没有返回值

  • 在使用 Thread 时,如果我们在长时间运行的方法中获得异常,则不可能在父函数中捕获该异常,但是如果我们在使用 Task ,则很容易捕获该异常。

  • Task 可取消任务执行,Thread 不行

  • Task 是基于 Thread 的,会使用底层的线程池。

  • Task 默认使用后台线程执行,Thread 默认使用前台线程

Unity 协程和 Task 的区别:

  • Unity 协程实际上是 IEnumrator 的可迭代函数,通过 StartCoroutine 方法将其注册进MonoBehaviour 的协程管理器,而 Task 是可以使用 async/await 还有 Task.Run 来创建异步任务

  • 协程是基于单线程的,在每一帧当中插入操作。Task 是基于线程池的

  • yield return 不能存在于任何 try-catch 代码块中,故协程也不能捕获异常,而 Task 可以捕获异常

3.委托的本质是什么,c#委托的底层实现思路是什么,委托和委托实例和事件三者的关系,为什么要使用委托

委托的本质: 委托就是一个密封类,其对象维护着一个可以引用一个或多个方法的字段

c# 委托的底层实现思路是什么: 底层通过一个密封类,其里面封装了一个 IntPtr 类型的指针,也就是个函数指针,可以引用方法。然后通过一个 object? 来维护调用此方法的对象(静态方法为 null

委托和委托实例和事件三者的关系:

  • 委托是一个密封类,委托的对象中维护着一个字段,其指向程序中另一个或多个以后会被调用的方法,可以使用 delegate 关键字声明一个委托类型,指定方法的参数和返回值类型。

  • 委托实例是委托类型的实例化对象,它引用一个或多个方法。

  • 事件是一种特殊的委托,在委托实例化时的时候加上 event 关键字,声明一个事件。其必须配合一个类来使用。

为什么要使用委托: 可以将方法作为另一个方法的参数来进行传递,这张方法动态地赋给参数的做法,可以避免在程序中大量使用if-elseSwitch 语句,同时使得程序具有更好的可扩展性

4.值类型和引用类型的基类分别是什么

引用类型的基类是 Object ,值类型的基类是 ValueType ,值类型也隐式继承自 Object

5.

Last updated