C#面试题及详细答案120道(86-95)-- 进阶特性
《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。

文章目录
- 一、本文面试题目录
- 86. 什么是扩展方法(Extension Method)?如何实现?
- 87. 什么是匿名类型?其特点是什么?
- 88. 动态类型(dynamic)的作用及使用场景
- 89. 什么是特性(Attribute)?如何自定义特性?
- 90. 反射(Reflection)的作用是什么?有什么优缺点?
- 91. 什么是依赖注入(DI)?C#中如何实现?
- 92. 什么是AOP(面向切面编程)?在C#中如何实现?
- 93. 简述C# 8.0及以上版本的新特性(如Nullable Reference Types、Async Streams等)
- 94. 什么是记录类型(Record)?与类的区别
- 95. 什么是顶级语句(Top-level Statements)?
- 二、120道C#面试题目录列表
一、本文面试题目录
86. 什么是扩展方法(Extension Method)?如何实现?
- 原理说明:扩展方法是一种特殊的静态方法,允许在不修改原有类型(包括密封类)的情况下,为其添加新的方法。扩展方法通过静态类中的静态方法实现,第一个参数使用
this关键字指定要扩展的类型。 - 实现步骤:
- 创建静态类(通常以“类型名+Extensions”命名)。
- 在静态类中定义静态方法,第一个参数为
this 目标类型 参数名。 - 调用时,扩展方法会像目标类型的实例方法一样被调用。
- 示例代码:
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 })。
- 示例代码:
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)。
- 与动态语言交互(如通过
- 示例代码:
using System;class Program
{static void Main(){// 动态类型变量dynamic obj = new { Name = "Test", Value = 100 };// 运行时解析成员,编译时不检查Console.WriteLine(obj.Name); // 输出:TestConsole.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特性指定目标元素(如类、方法)和是否可重复使用。 - 添加构造函数和属性存储元数据。
- 定义继承自
- 示例代码:
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)。
- 动态创建对象(如
- 优缺点:
- 优点:灵活性高,可处理编译时未知的类型。
- 缺点:性能开销较大(比直接调用慢),编译时无法检查类型安全性,代码可读性降低。
- 示例代码:
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等,自动管理依赖生命周期和注入。
- 示例代码:
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):
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>。string? nullableStr = null; // 允许为null string nonNullableStr = "hello"; // 不允许为null(编译时检查) -
异步流(Async Streams):
结合IAsyncEnumerable<T>和await foreach,支持异步枚举数据(如分页加载数据库数据)。async 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); } -
默认接口方法:
允许在接口中定义带实现的方法,不破坏现有实现类。public 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、实体) 实现业务逻辑 - 示例代码:
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); // 输出:TrueConsole.WriteLine(p1 == p3); // 输出:False// 不可变性:无法修改属性(除非定义为with表达式)var p4 = p1 with { Age = 31 }; // 创建新实例,修改AgeConsole.WriteLine(p4.Age); // 输出:31}
}
95. 什么是顶级语句(Top-level Statements)?
- 原理说明:顶级语句是C# 9.0引入的简化语法,允许在程序入口文件中直接编写代码,无需显式定义
Main方法和命名空间,编译器会自动生成入口点。 - 特点:
- 每个项目只能有一个文件包含顶级语句。
- 代码执行顺序即语句顺序,相当于
Main方法的内容。 - 支持异步(可直接使用
await,编译器生成async Task Main)。 - 可访问命令行参数(通过隐式
args变量)。
- 示例代码:
// 顶级语句示例(无需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("操作完成");
上述代码等价于:
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("操作完成");}
}
二、120道C#面试题目录列表
| 文章序号 | C#面试题120道 |
|---|---|
| 1 | C#面试题及详细答案120道(01-10) |
| 2 | C#面试题及详细答案120道(11-20) |
| 3 | C#面试题及详细答案120道(21-30) |
| 4 | C#面试题及详细答案120道(31-40) |
| 5 | C#面试题及详细答案120道(41-50) |
| 6 | C#面试题及详细答案120道(51-60) |
| 7 | C#面试题及详细答案120道(61-75) |
| 8 | C#面试题及详细答案120道(76-85) |
| 9 | C#面试题及详细答案120道(86-95) |
| 10 | C#面试题及详细答案120道(96-105) |
| 11 | C#面试题及详细答案120道(106-115) |
| 12 | C#面试题及详细答案120道(116-120) |
