C#面试题及详细答案120道(86-95)-- 进阶特性

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括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关键字指定要扩展的类型。
  • 实现步骤
    1. 创建静态类(通常以"类型名+Extensions"命名)。
    2. 在静态类中定义静态方法,第一个参数为this 目标类型 参数名
    3. 调用时,扩展方法会像目标类型的实例方法一样被调用。
  • 示例代码
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 { ... }语法创建。
  • 特点
    1. 隐式类型:编译器自动生成密封类,名称不可见。
    2. 只读属性:属性在创建时初始化,之后不可修改。
    3. Equals和GetHashCode重写:基于属性值比较,相同属性值的匿名对象视为相等。
    4. 常用于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组件的交互,避免复杂的反射代码。
  • 使用场景
    1. 与动态语言交互(如通过dynamic调用Python脚本函数)。
    2. 处理COM对象(如Office自动化)。
    3. 简化反射操作(替代复杂的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)是一种用于为代码元素(类、方法、属性等)添加元数据的机制。元数据可在运行时通过反射获取,用于实现如序列化、权限验证、代码分析等功能。
  • 自定义特性步骤
    1. 定义继承自Attribute的类,类名通常以"Attribute"结尾。
    2. (可选)应用AttributeUsage特性指定目标元素(如类、方法)和是否可重复使用。
    3. 添加构造函数和属性存储元数据。
  • 示例代码
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命名空间实现。
  • 作用
    1. 动态创建对象(如Activator.CreateInstance)。
    2. 动态调用方法或访问属性(无需编译时知道类型)。
    3. 读取特性(Attribute)元数据。
    4. 实现插件系统(动态加载 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#中如何实现?

  • 原理说明 :依赖注入是一种设计模式,用于减少类之间的耦合。核心思想是:类不直接创建依赖对象,而是通过外部传入(注入),使类更易于测试和维护。
  • 实现方式
    1. 构造函数注入:通过构造函数参数传入依赖(最常用)。
    2. 属性注入:通过公共属性设置依赖。
    3. 方法注入:通过方法参数传入依赖。
    4. 使用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#实现方式
    1. 特性+反射:自定义特性标记目标方法,运行时通过反射拦截并执行切面逻辑。
    2. 动态代理:使用库(如Castle DynamicProxy)生成代理类,在代理中嵌入切面逻辑。
    3. .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主要特性

    1. 可空引用类型(Nullable Reference Types)

      允许显式标记引用类型是否可空(如string?表示可空字符串),编译时检查可能的NullReferenceException,默认禁用,需在项目文件中设置<Nullable>enable</Nullable>

      csharp 复制代码
      string? nullableStr = null; // 允许为null
      string nonNullableStr = "hello"; // 不允许为null(编译时检查)
    2. 异步流(Async Streams)

      结合IAsyncEnumerable<T>await foreach,支持异步枚举数据(如分页加载数据库数据)。

      csharp 复制代码
      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);
      }
    3. 默认接口方法

      允许在接口中定义带实现的方法,不破坏现有实现类。

      csharp 复制代码
      public interface ILogger
      {
          void Log(string message);
          
          // 默认方法
          void LogWarning(string message)
          {
              Log($"Warning: {message}");
          }
      }
  • C# 9.0+补充特性

    • 记录类型(Record):不可变数据类型,自动实现相等性检查(见94题)。
    • 顶级语句(Top-level Statements) :简化程序入口,无需显式Main方法(见95题)。
    • 模式匹配增强 :如notandor模式,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方法和命名空间,编译器会自动生成入口点。
  • 特点
    1. 每个项目只能有一个文件包含顶级语句。
    2. 代码执行顺序即语句顺序,相当于Main方法的内容。
    3. 支持异步(可直接使用await,编译器生成async Task Main)。
    4. 可访问命令行参数(通过隐式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("操作完成");
    }
}

二、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)
相关推荐
我是唐青枫4 小时前
C#.NET ControllerBase 深入解析:Web API 控制器的核心基石
c#·.net
O败者食尘D5 小时前
【C#】使用Enigma将Winform或WPF打包成一个exe
c#
The Sheep 20238 小时前
C# 吃一堑,长一智
c#
q***829114 小时前
如何使用C#与SQL Server数据库进行交互
数据库·c#·交互
hixiong12317 小时前
C# OpenCVSharp实现Hand Pose Estimation Mediapipe
开发语言·opencv·ai·c#·手势识别
baivfhpwxf202317 小时前
SQL Server 服务端如何在其他电脑连接
c#
Dm_dotnet18 小时前
WPF/C#:使用Microsoft Agent Framework框架创建一个带有审批功能的终端Agent
c#
Dm_dotnet18 小时前
WPF/C#:使用Stylet中的IWindowManager用于显示等待窗体、对话框与消息框
c#
Jackson@ML18 小时前
360度看C#编程语言
开发语言·c#