《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux... 。

文章目录
- 一、本文面试题目录
-
-
- [86. 什么是扩展方法(Extension Method)?如何实现?](#86. 什么是扩展方法(Extension Method)?如何实现?)
- [87. 什么是匿名类型?其特点是什么?](#87. 什么是匿名类型?其特点是什么?)
- [88. 动态类型(dynamic)的作用及使用场景](#88. 动态类型(dynamic)的作用及使用场景)
- [89. 什么是特性(Attribute)?如何自定义特性?](#89. 什么是特性(Attribute)?如何自定义特性?)
- [90. 反射(Reflection)的作用是什么?有什么优缺点?](#90. 反射(Reflection)的作用是什么?有什么优缺点?)
- 91. 什么是依赖注入(DI)?C#中如何实现?
- 92. 什么是AOP(面向切面编程)?在C#中如何实现?
- [93. 简述C# 8.0及以上版本的新特性(如Nullable Reference Types、Async Streams等)](# 8.0及以上版本的新特性(如Nullable Reference Types、Async Streams等))
- [94. 什么是记录类型(Record)?与类的区别](#94. 什么是记录类型(Record)?与类的区别)
- [95. 什么是顶级语句(Top-level Statements)?](#95. 什么是顶级语句(Top-level Statements)?)
-
- 二、120道C#面试题目录列表
一、本文面试题目录
86. 什么是扩展方法(Extension Method)?如何实现?
- 原理说明 :扩展方法是一种特殊的静态方法,允许在不修改原有类型(包括密封类)的情况下,为其添加新的方法。扩展方法通过静态类中的静态方法实现,第一个参数使用
this关键字指定要扩展的类型。 - 实现步骤 :
- 创建静态类(通常以"类型名+Extensions"命名)。
- 在静态类中定义静态方法,第一个参数为
this 目标类型 参数名。 - 调用时,扩展方法会像目标类型的实例方法一样被调用。
- 示例代码:
csharp
using System;
// 扩展方法所在的静态类
public static class StringExtensions
{
// 为string类型添加扩展方法:反转字符串
public static string Reverse(this string input)
{
if (string.IsNullOrEmpty(input))
return input;
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
class Program
{
static void Main()
{
string text = "hello";
// 调用扩展方法,如同string自带方法
string reversed = text.Reverse();
Console.WriteLine(reversed); // 输出:olleh
}
}
87. 什么是匿名类型?其特点是什么?
- 原理说明 :匿名类型是没有显式定义名称的临时数据类型,用于存储一组只读属性的临时数据,通常通过
new { ... }语法创建。 - 特点 :
- 隐式类型:编译器自动生成密封类,名称不可见。
- 只读属性:属性在创建时初始化,之后不可修改。
- Equals和GetHashCode重写:基于属性值比较,相同属性值的匿名对象视为相等。
- 常用于LINQ :在查询中投影临时结果(如
select new { Name = p.Name, Age = p.Age })。
- 示例代码:
csharp
using System;
class Program
{
static void Main()
{
// 创建匿名类型对象
var person = new { Name = "Alice", Age = 30, IsStudent = false };
// 访问属性(编译器推断类型)
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
// 匿名类型数组
var people = new[]
{
new { Name = "Bob", Age = 25 },
new { Name = "Charlie", Age = 35 }
};
foreach (var p in people)
{
Console.WriteLine(p.Name);
}
}
}
88. 动态类型(dynamic)的作用及使用场景
- 原理说明 :
dynamic类型是C# 4.0引入的动态类型,编译器不会在编译时检查其成员(属性、方法),而是在运行时动态解析,类似弱类型语言的行为。 - 作用:简化与动态语言(如Python、JavaScript)或COM组件的交互,避免复杂的反射代码。
- 使用场景 :
- 与动态语言交互(如通过
dynamic调用Python脚本函数)。 - 处理COM对象(如Office自动化)。
- 简化反射操作(替代复杂的
Type.InvokeMember)。
- 与动态语言交互(如通过
- 示例代码:
csharp
using System;
class Program
{
static void Main()
{
// 动态类型变量
dynamic obj = new { Name = "Test", Value = 100 };
// 运行时解析成员,编译时不检查
Console.WriteLine(obj.Name); // 输出:Test
Console.WriteLine(obj.Value); // 输出:100
// 动态调用方法(若方法不存在,运行时抛RuntimeBinderException)
dynamic calculator = new Calculator();
int result = calculator.Add(2, 3);
Console.WriteLine(result); // 输出:5
}
}
public class Calculator
{
public int Add(int a, int b) => a + b;
}
89. 什么是特性(Attribute)?如何自定义特性?
- 原理说明:特性(Attribute)是一种用于为代码元素(类、方法、属性等)添加元数据的机制。元数据可在运行时通过反射获取,用于实现如序列化、权限验证、代码分析等功能。
- 自定义特性步骤 :
- 定义继承自
Attribute的类,类名通常以"Attribute"结尾。 - (可选)应用
AttributeUsage特性指定目标元素(如类、方法)和是否可重复使用。 - 添加构造函数和属性存储元数据。
- 定义继承自
- 示例代码:
csharp
using System;
// 自定义特性:标记方法的作者和创建日期
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class AuthorInfoAttribute : Attribute
{
public string Author { get; }
public DateTime CreateDate { get; }
// 构造函数
public AuthorInfoAttribute(string author, string createDate)
{
Author = author;
CreateDate = DateTime.Parse(createDate);
}
}
public class DocumentProcessor
{
// 应用自定义特性
[AuthorInfo("Alice", "2023-01-15")]
public void Process()
{
Console.WriteLine("Processing document...");
}
}
// 通过反射读取特性
class Program
{
static void Main()
{
var method = typeof(DocumentProcessor).GetMethod("Process");
var attribute = (AuthorInfoAttribute)Attribute.GetCustomAttribute(
method, typeof(AuthorInfoAttribute));
Console.WriteLine($"作者: {attribute.Author}, 创建日期: {attribute.CreateDate}");
}
}
90. 反射(Reflection)的作用是什么?有什么优缺点?
- 原理说明 :反射是指程序在运行时动态获取类型信息(如属性、方法、构造函数)并操作其成员的能力。通过
System.Reflection命名空间实现。 - 作用 :
- 动态创建对象(如
Activator.CreateInstance)。 - 动态调用方法或访问属性(无需编译时知道类型)。
- 读取特性(Attribute)元数据。
- 实现插件系统(动态加载 assemblies)。
- 动态创建对象(如
- 优缺点 :
- 优点:灵活性高,可处理编译时未知的类型。
- 缺点:性能开销较大(比直接调用慢),编译时无法检查类型安全性,代码可读性降低。
- 示例代码:
csharp
using System;
using System.Reflection;
public class MyClass
{
public string Name { get; set; }
public void Greet(string message)
{
Console.WriteLine($"Greeting: {message}");
}
}
class Program
{
static void Main()
{
// 获取类型信息
Type type = typeof(MyClass);
// 动态创建对象
object instance = Activator.CreateInstance(type);
// 动态设置属性
PropertyInfo nameProp = type.GetProperty("Name");
nameProp.SetValue(instance, "Reflection Test");
Console.WriteLine(nameProp.GetValue(instance)); // 输出:Reflection Test
// 动态调用方法
MethodInfo greetMethod = type.GetMethod("Greet");
greetMethod.Invoke(instance, new object[] { "Hello from reflection" });
}
}
91. 什么是依赖注入(DI)?C#中如何实现?
- 原理说明 :依赖注入是一种设计模式,用于减少类之间的耦合。核心思想是:类不直接创建依赖对象,而是通过外部传入(注入),使类更易于测试和维护。
- 实现方式 :
- 构造函数注入:通过构造函数参数传入依赖(最常用)。
- 属性注入:通过公共属性设置依赖。
- 方法注入:通过方法参数传入依赖。
- 使用DI容器 :如.NET内置的
IServiceCollection、Autofac等,自动管理依赖生命周期和注入。
- 示例代码:
csharp
using System;
// 依赖接口
public interface ILogger
{
void Log(string message);
}
// 依赖实现
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Log: {message}");
}
}
// 依赖注入的类(通过构造函数注入)
public class OrderService
{
private readonly ILogger _logger;
// 构造函数接收依赖,而非内部创建
public OrderService(ILogger logger)
{
_logger = logger;
}
public void ProcessOrder()
{
_logger.Log("Order processed");
}
}
// 使用示例
class Program
{
static void Main()
{
// 手动注入依赖
ILogger logger = new ConsoleLogger();
var orderService = new OrderService(logger);
orderService.ProcessOrder(); // 输出:Log: Order processed
// 使用.NET内置DI容器(简化版)
var services = new Microsoft.Extensions.DependencyInjection.ServiceCollection();
services.AddSingleton<ILogger, ConsoleLogger>();
services.AddTransient<OrderService>();
var provider = services.BuildServiceProvider();
var service = provider.GetService<OrderService>();
service.ProcessOrder();
}
}
92. 什么是AOP(面向切面编程)?在C#中如何实现?
- 原理说明:AOP(Aspect-Oriented Programming)是一种编程范式,通过分离"核心业务逻辑"和"横切关注点"(如日志、事务、缓存),实现代码复用和集中管理。横切关注点被封装为"切面",动态植入核心逻辑中。
- C#实现方式 :
- 特性+反射:自定义特性标记目标方法,运行时通过反射拦截并执行切面逻辑。
- 动态代理:使用库(如Castle DynamicProxy)生成代理类,在代理中嵌入切面逻辑。
- .NET拦截器 :如
IInterceptor(Unity容器)或AspectCore框架。
- 示例代码(特性+反射实现简单AOP):
csharp
using System;
using System.Reflection;
// 日志切面特性
[AttributeUsage(AttributeTargets.Method)]
public class LogAttribute : Attribute { }
// 业务类
public class OrderProcessor
{
[Log] // 应用日志切面
public void Process()
{
Console.WriteLine("Processing order...");
}
}
// AOP处理器
public static class AopProcessor
{
public static void ExecuteWithLogging(object instance, string methodName)
{
Type type = instance.GetType();
MethodInfo method = type.GetMethod(methodName);
// 检查是否有Log特性
if (method.IsDefined(typeof(LogAttribute), false))
{
// 切面逻辑:前置通知
Console.WriteLine("Log: Before method execution");
// 执行核心业务逻辑
method.Invoke(instance, null);
// 切面逻辑:后置通知
Console.WriteLine("Log: After method execution");
}
else
{
method.Invoke(instance, null);
}
}
}
// 使用示例
class Program
{
static void Main()
{
var processor = new OrderProcessor();
AopProcessor.ExecuteWithLogging(processor, "Process");
}
}
93. 简述C# 8.0及以上版本的新特性(如Nullable Reference Types、Async Streams等)
-
C# 8.0主要特性:
-
可空引用类型(Nullable Reference Types) :
允许显式标记引用类型是否可空(如
string?表示可空字符串),编译时检查可能的NullReferenceException,默认禁用,需在项目文件中设置<Nullable>enable</Nullable>。csharpstring? nullableStr = null; // 允许为null string nonNullableStr = "hello"; // 不允许为null(编译时检查) -
异步流(Async Streams) :
结合
IAsyncEnumerable<T>和await foreach,支持异步枚举数据(如分页加载数据库数据)。csharpasync IAsyncEnumerable<int> GenerateNumbersAsync() { for (int i = 0; i < 5; i++) { await Task.Delay(100); yield return i; } } // 使用 await foreach (var num in GenerateNumbersAsync()) { Console.WriteLine(num); } -
默认接口方法 :
允许在接口中定义带实现的方法,不破坏现有实现类。
csharppublic interface ILogger { void Log(string message); // 默认方法 void LogWarning(string message) { Log($"Warning: {message}"); } }
-
-
C# 9.0+补充特性:
- 记录类型(Record):不可变数据类型,自动实现相等性检查(见94题)。
- 顶级语句(Top-level Statements) :简化程序入口,无需显式
Main方法(见95题)。 - 模式匹配增强 :如
not、and、or模式,var模式等。
94. 什么是记录类型(Record)?与类的区别
-
原理说明 :记录类型(Record)是C# 9.0引入的引用类型,专为存储数据设计,默认实现不可变性和值相等性,语法上使用
record关键字定义。 -
与类的核心区别 :
特性 记录(Record) 类(Class) 不可变性 默认不可变(属性为 init,仅初始化时可改)默认可变(属性可读写) 相等性 值相等(比较属性值) 引用相等(比较对象地址) 继承 支持(但推荐用于封闭层次结构) 完全支持 用途 存储数据(如DTO、实体) 实现业务逻辑 -
示例代码:
csharp
using System;
// 定义记录类型
public record Person(string Name, int Age);
class Program
{
static void Main()
{
var p1 = new Person("Alice", 30);
var p2 = new Person("Alice", 30);
var p3 = new Person("Bob", 25);
// 值相等性(记录特性)
Console.WriteLine(p1 == p2); // 输出:True
Console.WriteLine(p1 == p3); // 输出:False
// 不可变性:无法修改属性(除非定义为with表达式)
var p4 = p1 with { Age = 31 }; // 创建新实例,修改Age
Console.WriteLine(p4.Age); // 输出:31
}
}
95. 什么是顶级语句(Top-level Statements)?
- 原理说明 :顶级语句是C# 9.0引入的简化语法,允许在程序入口文件中直接编写代码,无需显式定义
Main方法和命名空间,编译器会自动生成入口点。 - 特点 :
- 每个项目只能有一个文件包含顶级语句。
- 代码执行顺序即语句顺序,相当于
Main方法的内容。 - 支持异步(可直接使用
await,编译器生成async Task Main)。 - 可访问命令行参数(通过隐式
args变量)。
- 示例代码:
csharp
// 顶级语句示例(无需class和Main)
using System;
using System.Threading.Tasks;
Console.WriteLine("Hello, Top-level Statements!");
// 访问命令行参数
if (args.Length > 0)
{
Console.WriteLine($"第一个参数: {args[0]}");
}
// 异步操作
await Task.Delay(1000);
Console.WriteLine("操作完成");
上述代码等价于:
csharp
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello, Top-level Statements!");
if (args.Length > 0)
{
Console.WriteLine($"第一个参数: {args[0]}");
}
await Task.Delay(1000);
Console.WriteLine("操作完成");
}
}