当前位置:首页 > 技术 > 正文内容

C#核心面试题汇总

访客 技术 2026年7月5日 1

一、反射机制

反射允许程序在运行时动态获取类型信息并操作对象。通过Assembly类可以动态加载程序集,创建实例并调用其方法。

// 动态加载程序集并创建实例
System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom(path);
IModule module = (IModule)asm.CreateInstance(classFullName);
module.Execute(this, groupId, parameters);

二、值类型与引用类型

2.1 值类型(System.ValueType)

值类型直接存储数据本身,包括:整数类型(int、long、short)、浮点类型(float、double、decimal)、布尔类型(bool)、字符类型(char)、结构体(struct)和枚举(enum)。值类型变量在栈上分配内存,由操作系统自动管理其生命周期,栈上的数据会随方法调用结束而自动释放。

2.2 引用类型(System.Reference)

引用类型存储的是数据的内存地址,包括:对象类型(object)、字符串(string)、数组(array)、类(class)、委托(delegate)和动态类型(dynamic)。引用类型的实际数据存储在堆内存中,由垃圾回收器(GC)负责分配和释放。

2.3 装箱与拆箱

将值类型转换为引用类型称为装箱,反之称为拆箱。这两个过程涉及类型转换和内存分配,会产生显著的性能开销。建议在实际开发中优先使用泛型集合来避免频繁的装箱拆箱操作。

三、dynamic与object的类型差异

C#中所有类型都派生自Object基类,因此任何类型的值都可以赋值给object变量,编译器会在编译阶段进行类型检查。dynamic类型则会在运行时跳过编译器的类型检查,由运行时环境解析成员访问。

public class TypeDifferenceDemo
{
    public static void PrintValue(string input)
    {
        Console.WriteLine(input);
    }
    
    static void Main()
    {
        // object类型示例
        object objValue = "Hello World";
        // 编译时检查,object类型不能直接隐式转换为string
        PrintValue(objValue);  // 编译错误
        PrintValue((string)objValue);  // 需要显式转换
        
        // dynamic类型示例
        dynamic dynValue = "Hello World";
        PrintValue(dynValue);  // 正常运行,运行时解析
        
        dynValue = 100;
        PrintValue(dynValue);  // 编译通过但运行时报错
    }
}

四、多线程编程方案

并行处理:同一时刻多个CPU核心同时执行不同指令,从宏观和微观角度看都是真正的同时执行

并发处理:同一时间段内多个任务交替执行,宏观上看起来同时进行,微观上是CPU快速切换执行上下文

4.1 Thread 类实现

Thread是.NET平台最基础的多线程实现方式,通过创建新线程来执行特定任务。

using System;
using System.Threading;

class ThreadDemo
{
    static void Main()
    {
        Thread worker = new Thread(ProcessTask);
        worker.Start();
        Console.WriteLine("主线程继续执行");
    }
    
    static void ProcessTask()
    {
        Console.WriteLine("工作线程执行中");
    }
}

特性说明

需要开发者手动管理线程的创建、启动和终止,适用于简单的后台任务场景。

4.2 ThreadPool线程池

线程池通过复用已有线程来减少线程创建销毁的性能开销,适合执行大量短周期的异步任务。

using System;
using System.Threading;

class ThreadPoolDemo
{
    static void Main()
    {
        ThreadPool.QueueUserWorkItem(ProcessTask);
        Console.WriteLine("主线程继续执行");
    }
    
    static void ProcessTask(object state)
    {
        Console.WriteLine("工作线程执行中");
    }
}

特性说明

自动管理线程生命周期,复用线程资源,适合高频率的小任务。

4.3 Task与Task<T>任务

Task是.NET推荐的异步编程模式,基于线程池实现,支持async/await语法糖和任务链式调用。

using System;
using System.Net.Http;
using System.Threading.Tasks;

class TaskDemo
{
    static async Task Main()
    {
        Task task = Task.Run(ProcessTask);
        await task;
        Console.WriteLine("主线程继续执行");
        
        string content = await FetchDataAsync("https://api.example.com");
        Console.WriteLine(content);
    }
    
    static void ProcessTask()
    {
        Console.WriteLine("工作线程执行中");
    }
    
    static async Task<string> FetchDataAsync(string url)
    {
        using HttpClient client = new HttpClient();
        return await client.GetStringAsync(url);
    }
}

特性说明

完美支持异步编程模式,避免UI线程阻塞,提供更好的响应性。支持任务并行和链式调用,内部自动管理线程池,适合I/O密集型操作如网络请求和文件读写。

4.4 Parallel并行类

Parallel类提供了简洁的并行编程接口,适用于并行执行循环迭代或批量任务。

using System;
using System.Threading.Tasks;

class ParallelDemo
{
    static void Main()
    {
        Parallel.For(0, 10, index =>
        {
            Console.WriteLine($"任务 {index} 正在执行");
        });
        Console.WriteLine("主线程继续执行");
    }
}

特性说明

自动将任务分配到多个线程并行执行,特别适合数据并行处理场景,如批量数据计算和图像处理等CPU密集型任务。

4.5 BackgroundWorker组件

BackgroundWorker是专门为WinForms/WPF设计的组件,支持后台执行耗时任务,同时提供进度报告和取消机制。

using System;
using System.ComponentModel;
using System.Threading;

class BackgroundWorkerDemo
{
    static void Main()
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += HandleWork;
        bw.RunWorkerCompleted += HandleComplete;
        bw.RunWorkerAsync();
        Console.WriteLine("主线程继续执行");
    }
    
    static void HandleWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("后台任务执行中");
        Thread.Sleep(2000);
    }
    
    static void HandleComplete(object sender, RunWorkerCompletedEventArgs e)
    {
        Console.WriteLine("后台任务已完成");
    }
}

特性说明

专为UI应用设计,支持进度更新回调和任务取消功能,适合需要与UI交互的后台操作。

4.6 Timer定时器

Timer用于实现周期性或定时执行的任务,适用于需要定期轮询的场景。

using System;
using System.Threading;

class TimerDemo
{
    static void Main()
    {
        Timer timer = new Timer(ExecuteTask, null, 0, 1000);
        Console.WriteLine("主线程继续执行");
        Console.ReadLine();
    }
    
    static void ExecuteTask(object state)
    {
        Console.WriteLine("定时任务执行中");
    }
}

特性说明

适合后台定时任务,如心跳检测、定期数据同步等场景。

4.7 PLINQ并行查询

PLINQ是LINQ的并行扩展版本,能够并行处理集合数据,充分发挥多核优势。

using System;
using System.Linq;

class PlinqDemo
{
    static void Main()
    {
        var numbers = Enumerable.Range(1, 100);
        var result = numbers.AsParallel()
                           .Where(n => n % 2 == 0)
                           .Select(n => n * n)
                           .ToList();
        Console.WriteLine($"结果: {string.Join(", ", result)}");
    }
}

特性说明

适用于大规模数据并行处理,特别适合CPU密集型的数据转换和计算场景。

五、异步编程模式

5.1 基于任务的异步模式TAP(推荐)

TAP模式自.NET Framework 4引入,是目前推荐的异步编程标准。基于Task/Task<T>类型,结合async/await关键字,提供简洁高效的异步编程体验。

// 使用Task实现并行处理
public static void ProcessParallelTasks()
{
    Task job1 = Task.Run(() =>
    {
        Console.WriteLine("任务1完成");
    });
    
    Task job2 = Task.Run(() =>
    {
        Console.WriteLine("任务2完成");
    });
    
    Task<int> job3 = Task.Factory.StartNew(() =>
    {
        Console.WriteLine("任务3完成");
        return 100;
    });
    
    Task.WaitAll(job1, job2, job3);
}

5.2 异步编程模型APM

APM是使用IAsyncResult接口实现异步操作的传统模式,需要手动编写回调函数。通过Begin/End方法对来封装异步操作。.NET Core已废弃BeginInvoke方法,不建议新项目使用。

Begin方法启动异步操作并返回IAsyncResult对象,可用于查询异步调用状态。End方法在异步操作完成后调用,获取执行结果,若操作未完成会阻塞调用线程。

public class AsyncReadDemo
{
    public void ReadFileAsync(string filePath)
    {
        FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        byte[] buffer = new byte[1024];
        fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback),
            new FileReadState { Stream = fs, Buffer = buffer });
    }
    
    private class FileReadState
    {
        public FileStream Stream { get; set; }
        public byte[] Buffer { get; set; }
    }
    
    private void ReadCallback(IAsyncResult result)
    {
        FileReadState state = (FileReadState)result.AsyncState;
        try
        {
            int bytesRead = state.Stream.EndRead(result);
            if (bytesRead > 0)
            {
                string content = Encoding.UTF8.GetString(state.Buffer, 0, bytesRead);
                Console.WriteLine($"读取内容: {content}");
            }
        }
        finally
        {
            state.Stream.Close();
        }
    }
}

5.3 基于事件的异步模式EAP

EAP模式要求方法名以Async结尾,配合事件、事件委托和EventArg派生类型使用,自动处理回调细节。该模式自.NET Framework 2.0引入,目前已被TAP模式取代。

class EventAsyncDemo
{
    static void Main()
    {
        var worker = new DataProcessor();
        worker.ProcessCompleted += OnProcessCompleted;
        worker.StartProcessAsync(20);
        Console.ReadLine();
    }
    
    private static void OnProcessCompleted(int result)
    {
        Console.WriteLine($"处理完成,结果: {result}");
    }
}

public class DataProcessor : Component
{
    public delegate void ProcessDelegate(int input);
    public event ProcessDelegate ProcessCompleted;
    
    public void StartProcessAsync(int input)
    {
        ThreadPool.QueueUserWorkItem(ProcessWork, input);
    }
    
    private void ProcessWork(object obj)
    {
        int input = (int)obj;
        int result = input * 2;
        ProcessCompleted?.Invoke(result);
    }
}

六、线程同步机制

多线程环境下,为保证共享资源的安全访问,需要使用同步机制。常见的线程安全问题包括数据竞争、死锁和资源争用等。

6.1 锁机制

锁机制通过限制同时访问共享资源的线程数量来实现同步,是最常用的同步手段。

  • lock关键字

lock是C#最简单的同步锁,基于Monitor类实现,语法简洁,适合临界区较小的场景。

private static readonly object syncObject = new object();
private static int count = 0;

public static void AddCount()
{
    lock (syncObject)
    {
        count++;
    }
}
  • Monitor类

Monitor是lock关键字的底层实现,提供了更精细的控制能力,支持超时设置等高级功能。

private static readonly object syncObject = new object();

public static void AddCount()
{
    bool acquired = false;
    try
    {
        Monitor.Enter(syncObject, ref acquired);
        count++;
    }
    finally
    {
        if (acquired)
        {
            Monitor.Exit(syncObject);
        }
    }
}

6.2 信号量(Semaphore)

信号量是一种计数锁,可以限制同时访问资源的线程数量,常用于控制数据库连接池或线程池的并发数,支持跨进程同步。

private static Semaphore semaphore = new Semaphore(2, 2);

public static void AccessResource()
{
    semaphore.WaitOne();
    try
    {
        // 访问共享资源
    }
    finally
    {
        semaphore.Release();
    }
}

6.3 互斥体(Mutex)

Mutex是操作系统级别的锁,支持跨进程使用,适合多个应用程序共享同一资源的场景。由于涉及系统内核调用,性能开销较大。

查看代码``` private static Mutex mutex = new Mutex();

public static void AccessSharedResource() { mutex.WaitOne(); try { // 访问共享资源 } finally { mutex.ReleaseMutex(); } }


单实例运行示例```
static void Main(string[] args)
{
    bool createdNew;
    Mutex instance = new Mutex(true, "SingleInstanceMutex", out createdNew);
    if (!createdNew)
    {
        Application.ExitThread();
        return;
    }
}

6.4 读写锁(ReaderWriterLock)

读写锁允许读操作并发执行,写操作独占访问。适合读多写少的场景,如缓存系统和配置管理,可以显著提升读取性能。ReaderWriterLockSlim是轻量级版本,性能更优。

private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
private static int sharedValue = 0;

public static int ReadValue()
{
    rwLock.EnterReadLock();
    try
    {
        return sharedValue;
    }
    finally
    {
        rwLock.ExitReadLock();
    }
}

public static void WriteValue(int value)
{
    rwLock.EnterWriteLock();
    try
    {
        sharedValue = value;
    }
    finally
    {
        rwLock.ExitWriteLock();
    }
}

6.5 事件等待句柄

事件用于线程间信号通知,允许一个线程等待另一个线程发出信号。

  • ManualResetEvent

手动重置事件,需要显式调用Reset方法关闭信号,每次Set只唤醒一个等待线程,适合一对多通知场景。

查看代码``` using System; using System.Threading;

class ManualEventDemo { private static ManualResetEvent signal = new ManualResetEvent(false);

static void Main()
{
    Thread worker = new Thread(DoWork);
    worker.Start();
    
    Console.WriteLine("主线程等待信号");
    signal.WaitOne();
    Console.WriteLine("主线程收到信号");
    
    worker.Join();
}

static void DoWork()
{
    Console.WriteLine("工作线程执行任务");
    Thread.Sleep(2000);
    Console.WriteLine("工作线程发出信号");
    signal.Set();
}

}


- AutoResetEvent

自动重置事件,每次Set自动重置状态,只唤醒一个等待线程。ManualResetEventSlim是轻量级版本,适合短时间等待场景。

查看代码```
using System;
using System.Threading;

class AutoEventDemo
{
    private static AutoResetEvent signal = new AutoResetEvent(false);
    
    static void Main()
    {
        Thread worker = new Thread(DoWork);
        worker.Start();
        
        Console.WriteLine("主线程等待信号");
        signal.WaitOne();
        Console.WriteLine("主线程收到信号");
        
        worker.Join();
    }
    
    static void DoWork()
    {
        Console.WriteLine("工作线程执行任务");
        Thread.Sleep(2000);
        Console.WriteLine("工作线程发出信号");
        signal.Set();
    }
}

6.6 屏障(Barrier)

Barrier让多个线程在某个同步点等待,直到所有参与者都到达后才继续执行,适合分阶段并行计算场景。

private static Barrier barrier = new Barrier(3);

public static void PhaseTask()
{
    Console.WriteLine("线程到达同步点");
    barrier.SignalAndWait();
    Console.WriteLine("线程继续执行");
}

6.7 原子操作(Interlocked)

原子操作是不可中断的最小执行单元,确保多线程环境下的数据一致性。Interlocked类提供了一系列原子操作方法,比锁机制性能更高。

private static int counter = 0;

public static void Increment()
{
    Interlocked.Increment(ref counter);
}

6.8 线程安全集合

System.Collections.Concurrent命名空间提供了多种线程安全的集合类,无需手动加锁即可在多线程环境下安全使用。

ConcurrentQueue是线程安全的FIFO队列,适合多线程生产者消费者场景。

private static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();

public static void Enqueue(int value)
{
    queue.Enqueue(value);
}

public static bool TryDequeue(out int value)
{
    return queue.TryDequeue(out value);
}

ConcurrentDictionary是线程安全的键值对集合,支持高并发的读写操作。

private static ConcurrentDictionary<string, int> dict = new ConcurrentDictionary<string, int>();

public static void AddOrUpdate(string key, int value)
{
    dict.AddOrUpdate(key, value, (k, old) => old + value);
}
标签: C#

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。