某外企.NET全栈技术一面全真面试题
这是小编收集整理某老外面试.NET全栈开发一面面试题。废话不多说,还是先列出面试问题,你可以先试答一下;答案在文末同意给出,
第一部分:C# 语法与面向对象基础(OOP)
- 显式接口实现:如果一个类同时实现两个接口,且这两个接口包含同名同签名的方法,在类中如何分别调用和实现这两个接口的方法?
- 参数修饰符:请详细阐述 C# 中
out和ref参数修饰符的核心区别。在方法调用时,两者对变量初始化的要求有何不同? - 字符串处理:
String与StringBuilder在内存模型和可变性上的本质区别是什么?请结合性能考虑,说明在拼接大量字符串时为何首选StringBuilder。 - 静态构造函数:静态构造函数有何特征?它何时被 CLR 调用?它与实例构造函数在执行顺序和访问修饰符上有何不同?
- 委托机制:什么是委托(Delegate)?它本质上是基于什么类型?简述委托的多播(Multicast)特性及其用途。
- 终结器与资源管理:
Finalize方法(终结器)在 C# 中是什么?它与Dispose模式在资源释放时机和确定性上有什么根本性差异? - 对象实例化过程:在 .NET 托管堆上,使用
new关键字创建一个对象时,CLR 在后台大致经历了哪几个核心步骤(涉及内存分配、类型指针同步、构造函数调用等)? - 方法重写与多态:(注:原题缺失具体代码,此处修正为通用考察点)在继承体系中,如果基类方法标记为
virtual,子类使用override重写。请说明当基类引用指向子类对象时,调用该方法的运行时分派机制,并给出代码示例阐述base关键字在重写中的用法。 - 引用类型赋值行为:当创建一个引用类型对象并将其赋值给另一个变量后,再对其中一个变量重新实例化(
new)或置为null,另一个变量的值或状态会受到怎样的影响?请说明引用传递和值传递在此场景下的区别。
第二部分:ASP.NET Core 与依赖注入(DI)
- 依赖注入:什么是依赖注入(DI)?请简述构造函数注入(Constructor Injection)和属性注入(Property Injection)的优缺点。
- 中间件管道:什么是 ASP.NET Core 中的中间件(Middleware)?请描述请求处理管道的执行顺序(即
Use与Run的委托链),以及next()委托的作用。 - 身份认证与授权:解释 Authentication(认证)与 Authorization(授权)的区别。在 .NET Core 中,你通常如何实现基于策略(Policy)的授权?
- Web API 安全:针对 Web API,你会采取哪些核心安全措施?请至少从认证方案(如 JWT)、输入验证、CORS 跨域配置、HTTPS 强制和防 CSRF 攻击五个维度简述。
- 服务生命周期:分别阐述 Transient、Scoped 和 Singleton 的生命周期特征。在 ASP.NET Core 中,
AddDbContext默认注册的是哪种生命周期?为什么? - Web 服务对比:简要对比 SOAP 与 RESTful 架构风格的核心差异(包括协议、消息格式、状态管理方面)。
第三部分:SQL Server 与数据库编程
- SQL 删除命令:DELETE、TRUNCATE 和 DROP 三者有何本质区别?请从操作类型(DML/DCL/DDL)、事务日志影响、触发器行为和空间回收四个方面进行区分。
- 索引结构:聚集索引(Clustered Index)与非聚集索引(Non-Clustered Index)在物理存储结构上的核心区别是什么?一个表最多能建立几个聚集索引?非聚集索引的叶子节点存储的是什么?
- 排名窗口函数:请详细对比
ROW_NUMBER()、RANK()和DENSE_RANK()在处理并列(Ties)情况时的序号生成逻辑差异,并举例说明。 - SQL 编程题:编写一条 SQL 查询语句,使用窗口函数
DENSE_RANK()查询 Employee 表中**第 N 高(例如第 3 高)**的薪水。
第四部分:编码实现题(手写代码)
- 算法题(字符串):编写一个 C# 方法,输入一个字符串,统计其中每个字符出现的次数,并最终仅打印出现过的唯一字符(去重)。要求不重复输出相同的字符。
- 算法题(矩阵):编写一个 C# 方法,实现给定二维矩阵(M x N)的转置操作,即将行转换为列,列转换为行。输入为 3x3 示例
[[1,2,3],[4,5,6],[7,8,9]],请输出转置后的矩阵。
第五部分:.NET 运行时与内存管理(GC)
- 垃圾回收机制:简述 .NET 中的垃圾回收(GC)机制。请分别解释第 0 代、第 1 代和第 2 代(Generations 0, 1, 2)的作用,以及 GC 触发升级的条件。
- 确定性资源释放:
IDisposable接口的作用是什么?标准的Dispose(bool disposing)模式是如何实现托管和非托管资源的统一释放的?
第六部分:工程实践与生产问题排查
- 代码审查(PR):在 Pull Request 代码审查中,你通常会关注哪些核心维度?(提示:代码规范、设计模式、潜在性能瓶颈、异常处理、单元测试覆盖等)
- 质量定义:你如何定义“高质量的代码”(Quality Code)?请列举你认为最重要的 3~5 个衡量指标。
- 环境不一致排查:假设代码在本地 Debug 环境运行完全正常,但部署到生产环境(Release 模式)后出现诡异 Bug,你会按照什么思维导图(步骤)去定位问题?(请考虑环境变量、配置文件、日志级别、框架版本差异等)
参考答案(按题号对应)
1. 使用显式接口实现(Explicit Interface Implementation)。语法为 void IInterfaceA.Method() 和 void IInterfaceB.Method()。调用时,必须将实例转换为对应的接口类型((IInterfaceA)obj.Method())才能调用,若使用类实例直接调用则无法访问显式实现的方法。
2.ref:变量在传入前必须被初始化,方法可读取并修改它。out:变量在传入前可以不初始化,方法在返回前必须为 out 参数赋值。out 常用于返回多个值(如 TryParse)。
3.String 是**不可变(Immutable)引用类型,每次修改(拼接、替换)都会在堆上产生新对象,原有对象等待 GC,适合少量操作。StringBuilder 是可变(Mutable)**类型,维护一个字符缓冲区,适合频繁拼接修改(循环内),性能远优于 String。
4. 静态构造函数无访问修饰符(默认 private),无参数,由 CLR 在第一次加载类(访问静态成员或创建实例)之前自动且只调用一次。它先于任何实例构造函数执行。
5. 委托本质上是类型安全的函数指针,派生自 System.MulticastDelegate。多播特性指一个委托实例可以引用多个方法(使用 += 合并),调用时会按顺序执行调用列表中的所有方法。
6.Finalize(终结器,即 ~ClassName())由 GC 自动在回收时调用,非确定性,不保证立即执行。Dispose 由开发者显式调用,确定性释放资源,通常配合 using 语句。标准做法是实现 Dispose 释放托管/非托管资源,并调用 GC.SuppressFinalize 抑制冗余的终结器开销。
7. ① 计算类型及其父类字段所占字节数;② 从托管堆分配内存(含对象头 SyncBlockIndex 和类型指针 MethodTablePointer);③ 初始化对象附加成员(SyncBlockIndex 置 0,TypeHandle 指向类型);④ 调用实例构造函数(依次从基类构造到派生类)。
8. 运行时多态:当基类引用指向子类对象时,调用的方法由实际堆上的对象类型决定(即子类 override 的方法),而非编译时的引用类型。示例:
Base b = new Derived(); b.Method(); // 输出 Derived.Method (如果 Method 是 virtual/override)
base.Method() 在子类重写中显式调用父类实现。
9. 引用类型变量存储的是堆内存地址。赋值操作拷贝的是地址。若 objA = objB,两者指向同一对象。若随后 objA = new MyClass(),objA 指向新地址,objB 不受影响,仍指向原对象。若修改 objA 的属性(而非重新赋值),objB 的属性会同步变化。
10. DI 是实现控制反转(IoC)的一种设计模式。构造函数注入强制依赖可见性,对象一旦创建其依赖便不可变,推荐使用。属性注入可选且可变,适合非必要依赖或需要修改依赖的场景(如单元测试中的 Mock)。
11. 中间件按添加顺序组成管道(Request Delegate)。请求进入时按从前到后执行,响应返回时按从后到前执行(若调用了 _next())。Use 可以调用下一个中间件,Run 是终结点(不调用 next)。
12. AuthN(认证)是“你是谁”(如登录验证用户名密码,生成 JWT Token)。AuthZ(授权)是“你能做什么”(如角色 [Authorize(Roles="Admin")] 或策略 [Authorize(Policy="CanEdit")])。授权基于认证结果。
13. ① JWT Bearer 认证;② 输入验证([ApiController] + 模型校验);③ CORS 策略限制可信域名;④ 强制 HTTPS(AddHttpsRedirection);⑤ 防 CSRF(同源/双提交 Cookie,若非浏览器 SPA 需考虑)及敏感数据脱敏/限流。
14. Transient(每次获取新实例)、Scoped(同请求/作用域内共享)、Singleton(全局单例)。AddDbContext 默认注册为 Scoped,因为 EF Core 上下文应遵循工作单元模式,且不是线程安全的,需随请求生命周期回收。
15. SOAP 基于 XML,严格规范(WSDL),支持复杂安全标准(WS-Security),传输层无关(HTTP/SMTP)。REST 基于 HTTP(通常 JSON),轻量灵活,利用 HTTP 动词(GET/POST/PUT/DELETE),无状态。现代微服务更倾向 REST。
**16.**DELETE(DML)可带 WHERE,逐行删除,记录日志,触发触发器,空间不立即释放。TRUNCATE(DDL)不可带条件,删除所有页,日志量小,不触发行触发器,重置 Identity,空间释放给对象。DROP(DDL)直接删除整个表结构和数据,回收所有空间。
17.聚集索引:叶子节点存整行数据,表内数据物理顺序与索引顺序一致,一个表只能有一个。非聚集索引:叶子节点存索引键值+聚集索引键(或行定位器),需回表(Lookup)查询数据,一个表可建多个。
18. 假设工资均为 1000 的并列第一。
ROW_NUMBER():给行编连续唯一序号(1,2,3,...),并列时随机排先后。RANK():产生不连续序号(1,1,3)——并列共享,跳过下一位。DENSE_RANK():产生连续序号(1,1,2)——并列共享,不跳过。
19.
SELECT SalaryFROM ( SELECT Salary, DENSE_RANK() OVER (ORDER BY Salary DESC) AS RNK FROM Employee) TWHERE RNK = N; -- 例如 N=3
20.
public static void PrintUniqueChars(string input){ var dict = new Dictionary<char, int>(); foreach (char c in input) dict[c] = dict.TryGetValue(c, out int count) ? count + 1 : 1; foreach (var kv in dict) Console.WriteLine($"{kv.Key}"); // 只打印键,不打印次数实现"不重复"}
21.
public static int[,] Transpose(int[,] matrix){ int rows = matrix.GetLength(0), cols = matrix.GetLength(1); int[,] result = new int[cols, rows]; for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) result[j, i] = matrix[i, j]; return result;}
22. GC 采用**分代(Generational)**策略:
- 第 0 代:新分配的对象,回收频率最高(发生 GC 时清理)。
- 第 1 代:逃过 1 次 0 代 GC 的对象(晋升)。
- 第 2 代:逃过多次 GC 的大对象(LOH 直接进 2 代)或长久存活对象。GC 会自动调整各代阈值。
23.IDisposable 接口定义 Dispose() 方法,用于提前释放非托管资源(如文件句柄、数据库连接、GDI 资源)。标准模式是 Dispose(bool disposing),disposing=true 时释放托管资源并调用基类 Dispose;disposing=false 时仅释放非托管资源(由终结器调用)。
24. 关注点:① 业务逻辑正确性;② 异常处理与日志记录是否完善;③ 性能(有无 N+1 查询、循环操作 DB);④ 安全性(SQL 注入、敏感信息泄露);⑤ 可维护性(命名规范、注释、单一职责);⑥ 单元测试是否同步更新。
25. 高质量代码标准:① 可读性(自解释、遵循命名规范);② 可维护性/可扩展性(低耦合高内聚,遵循 OCP 开闭原则);③ 鲁棒性(优雅处理边界条件和异常);④ 高性能;⑤ 单元测试覆盖率达标。
26. 排查步骤:① 检查配置文件差异(appsettings.Production.json 与 appsettings.Development.json);② 核对环境变量(ASPNETCORE_ENVIRONMENT);③ 比对日志级别(Production 可能关闭了 Debug 日志导致信息丢失,需开启 Info 日志);④ 确认数据库连接字符串权限及迁移是否执行;⑤ 检查依赖的第三方服务/缓存可用性;⑥ 对比 TargetFramework 或 SDK 版本在生产与本地是否一致。
注:答案仅供参考

关注公众号↑↑↑:DotNet开发跳槽❀