本文介绍的最新高级C#面试知识点通常涵盖了语言本身的深入理解以及与.NET框架相关的高级主题。以下是一些高级C#面试知识点。
高级C#面试知识点
这些知识点涵盖了C#编程的广泛领域,根据您所面试的具体职位和公司需求,您可能需要更深入地了解其中的一些方面。在面试前,建议您复习这些知识点并准备好能够清晰解释和演示这些概念的示例。
一、多线程
在介绍多线程之前,简单的说一些概念,这些概念有利于你在Windows环境下编程更好的理解基础性的知识,当然,Linux环境下编译也是相通的。
1、进程:当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。
2、线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
3、句柄:句柄是Windows系统中对象或实例的标识,这些对象包括模块、应用程序实例、窗口、控制、位图、GDI对象、资源、文件等。
4、多线程
(4.1)多线程概念
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
(4.2)多线程优点
可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。(牺牲空间计算资源,来换取时间)
(4.3)多线程缺点
线程也是程序,所以线程运行需要占用计算机资源,线程越多占用资源也越多。(占内存多)
多线程需要协调和管理,所以需要CPU跟踪线程,消耗CPU资源。(占cpu多)
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。多线程存在资源共享问题)
线程太多会导致控制太复杂,最终可能造成很多Bug。(管理麻烦,容易产生bug)
(4.4)为什么计算机可以多线程
程序运行需要计算机资源,操作系统就会去申请CPU来处理,CPU在执行动作的时候是分片执行的。
分片:把CPU的处理能力进行切分,操作系统在调度的时候,按照切片去处理不同的计算需求,按照规则分配切片计算资源,对于同一个计算机核心来讲,所有的运行都是串行的,但是因为分片的存在,感觉几个程序同时在向前推进。
(5)何时建议使用多线程
当主线程试图执行冗长的操作,但系统会卡界面,体验非常不好,这时候可以开辟一个新线程,来处理这项冗长的工作。
当请求别的数据库服务器、业务服务器等,可以开辟一个新线程,让主线程继续干别的事。
利用多线程拆分复杂运算,提高计算速度。
(6)何时不建议使用多线程
当单线程能很好解决,就不要为了使用多线程而用多线程。
5、同步,异步
(5.1)同步方法
线性执行,从上往下依次执行,同步方法执行慢,消耗的计算机资源少。
(5.2)异步方法
线程和线程之间,不再线型执行,多个线程总的耗时少,执行快,消耗的计算机资源多,各线程执行是无序的。
6、C#中的多线程
Thread/ThreadPool/Task 都是C#语言在操作计算机的资源时封装的帮助类库。
Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task,但从多线程完整性的角度上来说,我们有必要了解下早期多线程的是怎么处理的,以便体会.Net体系中多线程处理方式的进化。
- 后台线程,界面关闭,线程也就随之消失
- 前台线程,界面关闭,线程会等待执行完才结束
- 设置优先级只是提高了他被优先执行的概率
- 为了解决多线程竞用共享资源的问题,引入数据槽的概念,即将数据存放到线程的环境块中,使该数据只能单一线程访问。
- AllocateNamedDataSlot命名槽位和AllocateDataSlot未命名槽位,在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问
- 利用特性[ThreadStatic],在主线程中给ThreadStatic特性标注的变量赋值,则只有主线程能访问该变量
- 利用ThreadLocal线程的本地存储,在主线程中声明ThreadLocal变量,并对其赋值,则只有主线程能访问该变量
.NET Framework2.0时代,出现了一个线程池ThreadPool,是一种池化思想,如果需要使用线程,就可以直接到线程池中去获取直接使用,如果使用完毕,在自动的回放到线程池去;
1、ThreadPool好处
不需要程序员对线程的数量管控,提高性能,防止滥用,去掉了很多在Thread中没有必要的Api
2、线程池如何分配一个线程
QueueUserWorkItem方法,将方法排入队列以便开启异步线程,它有两个重载。
QueueUserWorkItem(WaitCallback callBack),WaitCallback是一个有一个object类型参数且无返回值的委托。
QueueUserWorkItem(WaitCallback callBack, object state),WaitCallback是一个有一个object类型参数且无返回值的委托,state即WaitCallback中需要的参数, 不推荐这么使用,存在拆箱装箱的转换问题,影响性能。
1、Task出现背景
在前面的章节介绍过,Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然也可以基本业务需要的多线程场景,但它们在多个线程的等待处理方面、资源占用方面、线程延续和阻塞方面、线程的取消方面等都显得比较笨拙,在面对复杂的业务场景下,显得有点捉襟见肘了。正是在这种背景下,Task应运而生。
Task是微软在.Net 4.0时代推出来的,也是微软极力推荐的一种多线程的处理方式,Task看起来像一个Thread,实际上,它是在ThreadPool的基础上进行的封装,Task的控制和扩展性很强,在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool。
Task复制文件应用参考:https://znlive.com/what-is-the-simplest-csharp-code-to-copy-directory
参考文章:
https://learn.microsoft.com/zh-cn/dotnet/standard/threading/threads-and-threading (推荐) 官网
https://blog.csdn.net/liyou123456789/article/details/120595489(推荐)重要
https://www.runoob.com/csharp/csharp-multithreading.html/https://www.w3schools.cn/cs/cs_multithreading.asp(推荐)
https://www.cnblogs.com/dotnet261010/p/6159984.html (可看可不看)
https://www.cnblogs.com/cheng8/p/16168128.html(可看可不看)
https://cloud.tencent.com/developer/article/1817868(可看可不看)
https://zhuanlan.zhihu.com/p/355962916(可看可不看)
二、虚方法和抽象方法
C#中的虚方法(Virtual Methods)和抽象方法(Abstract Methods)都与面向对象编程中的多态性和继承概念相关。它们允许您在基类中定义方法,然后在派生类中进行扩展或覆盖,以实现特定的行为。以下是对这两种方法的详细描述:
2.1 虚方法(Virtual Methods)
- 定义方式:虚方法是在基类中使用
virtual
关键字声明的方法。例如:
public class Animal { public virtual void MakeSound() { Console.WriteLine("Some generic animal sound -- from jhrs.com "); } }
- 派生类的重写:派生类可以使用
override
关键字重写虚方法,以提供特定于派生类的实现。例如:
public class Dog : Animal { public override void MakeSound() { Console.WriteLine("Bark -- from jhrs.com"); } }
- 多态性:虚方法支持多态性,这意味着您可以通过基类引用调用派生类中的方法。例如:
Animal myAnimal = new Dog(); myAnimal.MakeSound(); // 调用的是Dog类中的MakeSound方法
- 默认实现:虚方法可以提供一个默认的实现,但派生类可以选择是否要重写它。
- 可选重写:派生类可以选择性地重写虚方法。如果不重写,将调用基类中的实现。
2.2 抽象方法(Abstract Methods)
- 定义方式:抽象方法是在抽象类中声明的方法,并使用
abstract
关键字进行标记。抽象方法没有方法体。例如:
public abstract class Shape { public abstract double CalculateArea(); }
- 派生类的实现:派生类必须实现抽象方法,否则它们也必须声明为抽象类。
- 强制实现:抽象方法强制派生类提供特定的实现,以确保每个派生类都有自己的版本。
- 不能实例化:抽象类不能被直接实例化,只能用作基类。
- 多态性:抽象方法支持多态性,可以使用基类引用来调用派生类中的方法,就像虚方法一样。
总之,虚方法提供了默认的实现,允许派生类选择是否要重写它,而抽象方法则强制派生类提供特定的实现。抽象方法通常用于定义一组方法的接口,而虚方法用于提供默认实现并允许派生类进行自定义。
三、接口和抽象类
在C#中,接口和抽象类都是用于实现多态性和组件重用的机制,但它们有一些关键区别。下面是对C#中接口和抽象类的详细描述:
3.1 接口(Interface)
- 定义方式:接口是一种完全抽象的类型,定义了一组公共方法、属性、事件和索引器的契约,但没有实际的方法实现。接口使用
interface
关键字定义,不包含字段或构造函数。例如:
public interface IDrawable { void Draw(); }
- 多继承:一个类可以同时实现多个接口。这允许类在不同的上下文中扮演不同的角色。例如:
public class Circle : IDrawable, IResizable { // jhrs.com首发, 实现IDrawable和IResizable接口中的方法 }
- 无状态:接口不能包含字段(成员变量)或属性的实现。它们只能定义方法、属性、事件和索引器的契约。
- 强制实现:类实现接口时,必须提供接口中定义的所有成员的具体实现。这确保了接口中定义的行为在类中得以实现。
- 多态性:接口允许不同的类实现相同的接口,以便通过接口引用来调用不同类的方法。
3.2 抽象类(Abstract Class)
- 定义方式:抽象类是一个类,它可以包含抽象方法(使用
abstract
关键字定义的方法)和具体方法的实现。抽象类使用abstract
关键字定义。例如:
public abstract class Shape { public abstract double CalculateArea(); public void Display() { Console.WriteLine("Displaying the shape. -- from jhrs.com"); } }
- 单继承:C#中的类只能继承一个基类,因此抽象类实现了单一继承。
- 有状态:抽象类可以包含字段、属性、构造函数以及具体方法的实现。这使得抽象类可以包含一些通用数据和行为。
- 可选实现:派生类可以选择性地重写抽象方法,而不是强制要求提供所有抽象方法的实现。
- 多态性:抽象类允许多态性,允许派生类通过基类引用来调用方法,包括抽象方法和具体方法。
- 构造函数:抽象类可以有构造函数,但接口不能包含构造函数。
3.3 何时使用接口和抽象类
- 使用接口当您希望定义一组契约,以确保不同的类都实现相同的方法。
- 使用抽象类当您希望提供一些通用的实现,并为派生类提供一些自由度,同时实现多态性。
总之,接口和抽象类都是面向对象编程的重要概念,您应该根据您的设计需求选择适当的方式来实现多态性和组件重用。
四、依赖注入和控制反转
依赖注入(DI):服务的消费者利用一个独立的容器(Container)来获取所需的服务对象,容器自身在提供服务对象的过程中会自动完成依赖的解析与注入(核心功能:服务注册和服务提供)
通俗来讲,就是把有依赖关系的类放到容器中,然后在我们需要这些类时,容器自动解析出这些类的实例。
依赖注入最大的好处时实现类的解耦,利于程序拓展、单元测试、自动化模拟测试等。
控制反转(IOC): 控制反转只是一个概念一种设计思想,也就是将创建对象实例的控制权(原本是程序员)从代码控制权剥离到 `IOC 容器` 中控制。
依赖倒置原则(DIP)
「依赖倒置」是本文要讲述的主要内容,是七大设计原则之二,在生产实际中应用的非常广泛,主要内容为
1. 高层模块(high-level modules)**不要直接依赖**低层模块(low-level);
2. 高层模块和低层模块应该**通过抽象(abstractions)来互相依赖**;
3. 抽象(abstractions)不要依赖具体实现细节(details),具体实现细节(details)依赖抽象(abstractions)。
在.NET Core中DI的核心分为两个组件:IServiceCollection和 IServiceProvider。
– IServiceCollection 负责注册
– IServiceProvider 负责提供实例
依赖注入的三种生命周期:
– Transient: 每一次GetService都会创建一个新的实例
– Scoped: 在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)
– Singleton :整个应用程序生命周期以内只创建一个实例
注入方式
一 、构造方法注入
目前构造方法注入是依赖注入推荐使用方式。
– 优点
– 在构造方法中体现出对其他类的依赖,一眼就能看出这个类需要依赖哪些类才能工作
– 脱离了 IOC 框架,这个类仍然可以工作,POJO 的概念
– 一旦对象初始化成功了,这个对象的状态肯定是正确的
– 缺点
– 构造函数会有很多参数(Bad smell)
– 有些类是需要默认构造函数的,比如 MVC 框架的 Controller 类,一旦使用构造函数注入,就无法使用默认构造函数
– 这个类里面的有些方法并不需要用到这些依赖(Bad smell)
二 、属性方式注入
通过属性方式注入容易和类的实例属性混淆,不建议使用。
– 优点
– 在对象的整个生命周期内,可以随时动态的改变依赖
– 非常灵活
– 缺点
– 对象在创建后,被设置依赖对象之前这段时间状态是不对的
– 不直观,无法清晰地表示哪些属性是必须的
三 、方法参数注入
方法参数注入的意思是在创建对象后,通过自动调用某个方法来注入依赖。
– 优点:
– 比较灵活
– 缺点:
– 新加入依赖时会破坏原有的方法签名,如果这个方法已经被其他很多模块用到就很麻烦
– 与构造方法注入一样,会有很多参数
IOC/DI的优缺点:
优点:
1、它有助于类的解耦。
2、由于解耦,代码的可重用性增加。
3、改进了代码可维护性和测试。
缺点
– 目前主流的 `IOC/DI` 基本采用反射的方式来实现依赖注入,在一定程度会影响性能
参考文章:
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0
https://learn.microsoft.com/zh-cn/dotnet/core/extensions/dependency-injection#service-lifetimes
https://www.cnblogs.com/cn-star/p/14699484.html
https://www.cnblogs.com/cwsheng/p/14224423.html
https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html
https://www.cnblogs.com/stulzq/p/12610026.html
五、中间件和过滤器
1、中间件
中间件是一种装配到应用管道以处理请求和响应的软件(应用程序管道中的一个组件,用来拦截请求过程进行一些其他处理和响应)。 每个组件:
– 选择是否将请求传递到管道中的下一个组件。
– 可在管道中的下一个组件前后执行工作。
2、中间件的四种运行方式
1、app.Use(),IApplicationBuilder接口原生提供,注册等都用它。
2、app.Run(),是一个扩展方法,它需要一个RequestDelegate委托,里面包含了Http的上下文信息,没有next参数,因为它总是在管道最后一步执行。
3、app.Map(),也是一个扩展方法,类似于MVC的路由,用途一般是一些特殊请求路径的处理。
4、app.UseMiddleware<>(),没错,就是这个了。 为什么说功能强大呢?是因为它不但提供了注册中间件的功能,还提供了依赖注入(DI)的功能,以后大部分情况就用它了。
asp.net core` 提供了`IApplicationBuilder`接口来让把中间件注册到`asp.net`的管道请求当中去,中间件是一个典型的AOP应用。 下面是一个微软官方的一个中间件管道请求图:
3、中间件(Middleware)和过滤器(Filter)的区别
熟悉MVC框架的同学应该知道,MVC也提供了5大过滤器供我们用来处理请求前后需要执行的代码。
分别是:AuthenticationFilter, AuthorizationFilter,ActionFilter, ExceptionFilter,ResultFilter。
根据描述,可以看出中间件和过滤器的功能类似,那么他们有什么区别?为什么又要搞一个中间件呢?
其实,过滤器和中间件他们的关注点是不一样的,也就是说职责不一样,干的事情就不一样。
同作为两个AOP利器,过滤器更贴合业务,它关注于应用程序本身,比如你看ActionFilter 和 ResultFilter,它是直接与控制器交互的,同时比如我们要对结果进行格式化啦,且要对请求的ViewModel进行数据验证啦,这个时候就是用Filter无疑了。它是MVC的一部分,它可以拦截到你控制器上下文的一些信息,而中间件是没有这个能力的。
中间件ASP.NET Core这个基础提供的功能,而Filter是ASP.NET Core MVC中提供的功能,ASP.NET Core MVC是由MVC中间件提供的框架,而Fiter属于MVC中间件提供的功能。
4、什么情况我们需要中间件
在我们的应用程序当中和业务关系不大的一些需要在管道中做的事情可以使用,比如身份验证,`Session`存储,日志记录等。
其实我们的 asp.net core项目中本身已经包含了很多个中间件,比如MVC本身就是一个中间件,过滤器是中间件的一部分。
参考文章:
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0(官网)
六、委托和事件
委托定义:[委托](https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/reference-types)是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容参数和返回类型的方法进行绑定。 你可以通过委托实例调用方法。
简单的理解,委托是方法的抽象类,它定义了方法的类型,可以实例化。和普通的类一样,可以申明变量进行赋值,可以当作参数传递,可以定义成属性。
委托具有以下属性:
– 委托类似于 C++ 函数指针,但委托完全面向对象,不像 C++ 指针会记住函数,委托会同时封装对象实例和方法。
– 委托允许将方法作为参数进行传递。
– 委托可用于定义回调方法。
– 委托可以链接在一起;例如,可以对一个事件调用多个方法。
– 方法不必与委托类型完全匹配。 有关详细信息,请参阅[使用委托中的变体](https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/covariance-contravariance/using-variance-in-delegates)。
– 使用 Lambda 表达式可以更简练地编写内联代码块。 Lambda 表达式(在某些上下文中)可编译为委托类型。 若要详细了解 lambda 表达式,请参阅 [lambda 表达式](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/lambda-expressions)。
事件定义:事件是基于委托的,为委托提供一个订阅或发布的机制,事件就相当于保存委托的数组,实际上,事件是建立在对委托的语言支持之上的一种设计而已。
总的来说
1.委托是一个类,可以实例化,通过委托的构造函数把方法赋值给委托实例;
2.触发委托有两种方式:委托实例.Invoke(参数列表)和委托实例(参数列表);
3.事件可看作是一个委托类型的变量;
4.+=为事件注册多个委托实例或多个方法;
5.-=为事件减少多个委托实例或多个方法;
5.EventHandler是一个委托。
委托与事件区别
通俗的解释:
1、事件的声明只是在委托前面加一个event关键词,虽然你可以定义一个public,但是有了event关键词后编译器始终会把这个委托声明为private,然后添加1组add,remove方法。add对应+=,remove对应-=。这样就导致事件只能用+=,-=来绑定方法或者取消绑定方法。而委托可以用=来赋值,当然委托也是可以用+=,-=来绑定方法的。 (保护委托字段,对外不开放,所以外部对象没法直接操作委托。提供了Add和Remove方法,供外部对象订阅事件和取消事件)
2、委托可以在外部被其他对象调用,而且可以有返回值(返回最后一个注册方法的返回值)。而事件不可以在外部调用,只能在声明事件的类内部被调用。
事件基于委托,但并非委托。(事件的处理方法在对象外部定义,而事件的执行是在对象的内部,至于事件的触发,何时何地无所谓。)
1.事件只能在方法的外部进行声明,而委托在方法的外部和内部都可以进行声明;
2.事件只能在类的内部进行触发,不能在类的外部进行触发。而委托在类的内部和外部都可触发;
3.委托一般用于回调,而事件一般用于外部接口。在观察者模式中,被观察者可在内部声明一个事件作为外部观察者注册的接口。
参考文章:
https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/delegates/
https://www.cnblogs.com/wangqiang3311/p/11201647.html
https://www.cnblogs.com/Bob-luo/archive/2023/02/17/17129790.html
https://zhuanlan.zhihu.com/p/398084318
【江湖人士】(jhrs.com)原创文章,作者:江小编,如若转载,请注明出处:https://jhrs.com/2023/47094.html
扫码加入电报群,让你获得国外网赚一手信息。
文章标题:15+最新高级C#面试知识点