C#基础面试题
1、反射
System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(info.filePath);
IModule iModule = (IModule)assembly.CreateInstance(info.className);
iModule.DoModule(this, info.moduleGroupID, info.param);
2、值类型和引用类型
2.1 值类型(System.ValueType)
- 整型、浮点型、布尔、char、struct、enum
- 变量存储实际的数据
- 实际数据存储在栈上,操作系统负责管理分配释放
2.2 引用类型(System.Reference)
- object、string、array、class、delegate、dynamic
- 变量存储的是数据的地址
- 实际数据存储在堆上,GC负责管理分配释放
2.3 装箱和拆箱
将值类型=>引用类型:装箱;引用类型=>值类型:拆箱。装箱和拆箱都需要进行大量的运算,装箱还需要分配和构造一个新对象,性能消耗更多。建议使用泛型。
3、dynamic和object的区别
C#中所有的类型(引用类型、值类型、预定义)都继承自Object。所以任何类型的值都可以用object来接收,编译时会进行类型检查。
dynamic动态类型会在编译时绕过类型检查,运行时解析。所以只存在于编译时,不存在于运行时。
public class TestDynamicAndObjectType
{
public static void ConsoleWrite(string inputArg)
{
Console.WriteLine(inputArg);
}
static void Main()
{
// object 类型演示
object obj = "追逐时光者";
// 编译错误 除非转化为 string 类型
ConsoleWrite(obj);// compile error
ConsoleWrite((string)obj); // correct
Console.ReadKey();
// dynamic 类型演示
dynamic dyn = "追逐时光者";
ConsoleWrite(dyn); // correct
dyn = 1;
// 编译时不报错, 运行时会报错
ConsoleWrite(dyn);// Runtime Error
Console.ReadKey();
}
}
4、多线程实现的几种方式
并行:同一时刻有多条指令在多个处理器上同时运行,无论宏观还是微观都是同时发生
并发:同一时段内,宏观上多个任务同时进行,微观上快速切换执行,同一时刻可能只有一条指令在执行
4.1 Thread 类
Thread 是 .NET 中最基础的多线程实现方式,直接创建一个新的线程来执行任务。
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread thread = new Thread(DoWork);
thread.Start();
Console.WriteLine("Main thread is running.");
}
static void DoWork()
{
Console.WriteLine("Worker thread is running.");
}
}
特点
-
需要手动控制线程的创建、启动和停止,管理线程的生命周期。
-
适用于简单的多线程任务。
4.2 ThreadPool
ThreadPool 是一个线程池,用于管理和复用线程,避免频繁创建和销毁线程的开销。
using System;
using System.Threading;
class Program
{
static void Main()
{
ThreadPool.QueueUserWorkItem(DoWork);
Console.WriteLine("Main thread is running.");
}
static void DoWork(object state)
{
Console.WriteLine("Worker thread is running.");
}
}
特点
- 自动管理线程的生命周期。
-
需要执行大量短时间任务。
-
避免频繁创建和销毁线程。
4.3 Task 和 Task<T>
Task 是 .NET 中推荐的多线程实现方式,基于线程池,支持异步编程和任务并行。
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Task task = Task.Run(DoWork);
await task;
Console.WriteLine("Main thread is running.");
string result = await DownloadContentAsync("https://example.com");
Console.WriteLine(result);
}
static void DoWork()
{
Console.WriteLine("Worker thread is running.");
}
static async Task<string> DownloadContentAsync(string url)
{
using HttpClient client = new HttpClient();
return await client.GetStringAsync(url);
}
}
特点
-
支持异步编程(async/await),避免阻塞主线程,提高响应性。
-
支持任务并行和任务链。
-
自动管理线程池。
-
适用于 I/O 密集型任务(如文件读写、网络请求)。
4.4 Parallel 类
Parallel 类提供了简单的并行编程模型,用于并行执行循环或任务。
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
Parallel.For(0, 10, i =>
{
Console.WriteLine($"Task {i} is running.");
});
Console.WriteLine("Main thread is running.");
}
}
特点
-
自动分配任务到多个线程。
-
适用于数据并行(如并行处理数组或集合)。
- 适用于 CPU 密集型任务(如计算、图像处理)。
4.5 BackgroundWorker
BackgroundWorker 是一个组件,用于在后台执行耗时操作,同时支持进度报告和取消操作。
using System;
using System.ComponentModel;
using System.Threading;
class Program
{
static void Main()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += WorkCompleted;
worker.RunWorkerAsync();
Console.WriteLine("Main thread is running.");
}
static void DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Worker thread is running.");
Thread.Sleep(2000); // 模拟耗时操作
}
static void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("Worker thread completed.");
}
}
特点
-
需要在 UI 应用程序中执行后台任务。适用于 WinForms 或 WPF 应用程序。
-
支持进度更新和任务取消。
4.6 Timer 类
Timer 用于定期执行任务,适用于定时任务或周期性任务。
using System;
using System.Threading;
class Program
{
static void Main()
{
Timer timer = new Timer(DoWork, null, 0, 1000);
Console.WriteLine("Main thread is running.");
Console.ReadLine(); // 防止主线程退出
}
static void DoWork(object state)
{
Console.WriteLine("Worker thread is running.");
}
}
特点
-
适用于后台任务或定时任务(如定时任务、心跳检测)。
4.7 Parallel LINQ (PLINQ)
PLINQ 是 LINQ 的并行版本,用于并行处理集合数据。
using System;
using System.Linq;
class Program
{
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($"Result: {string.Join(", ", result)}");
}
}
特点
- 适用于数据并行处理。
-
适用于 CPU 密集型任务。
5、异步模型
5.1 基于任务的异步模型TAP(推荐)
TAP(Task-based Asynchronous Pattern)是.net framework4 引入的,是 .NET 中推荐使用的异步编程模型,基于 Task 和 Task<T> 类型,结合 async 和 await 关键字,提供了一种简洁、高效的异步编程方式。
示例:通过Task和Task<T>实现异步编程,可利用多核处理器,执行多个并发任务
/// <summary>
/// 任务并行库(TPL, Task Parallel Library TAP模式)
/// </summary>
public static void TestTaskParallel()
{
var task1 = Task.Run(() =>
{
Console.WriteLine("Task 1 Completed.");
});
var task2 = Task.Run(() =>
{
Console.WriteLine("Task 2 Completed.");
});
Task<int> task3 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task 3 Completed.");
return 20; // 返回一个整数值
});
//等待所有任务完成
Task.WaitAll(task1, task2, task3);
}
5.2 异步编程模型AMP
是使用IAsyncResult接口提供异步行为的旧模型,需要手动创建回调函数。在这种模式下,需要通过Begin和End方法实现同步操作(如BeginWrite和EndWrite实现异步写入操作)。不建议新的开发使用这种模式。.net core以后BeginEnvoke已摒弃使用。
Begin启动异步操作,返回IAsyncResult接口,可用于检测异步调用的过程。
End在异步操作完成后执行,检测异步调用的结果。如果异步调用尚未完成,EndInvoke将阻塞调用线程,直到它完成。
public void ReadFileAsync(string filePath)
{
// 创建文件流
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
// 创建缓冲区
byte[] buffer = new byte[1024];
// 开始异步读取
fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCompleted),
new AsyncState { FileStream = fs, Buffer = buffer });//IAsyncResult.AsyncState:用户自定义的状态对象。
}
private class AsyncState
{
public FileStream FileStream { get; set; }
public byte[] Buffer { get; set; }
}
private void ReadCompleted(IAsyncResult ar)
{
AsyncState state = (AsyncState)ar.AsyncState;
try
{
// 完成读取操作
int bytesRead = state.FileStream.EndRead(ar);
if (bytesRead > 0)
{
// 处理读取到的数据
string content = Encoding.UTF8.GetString(state.Buffer, 0, bytesRead);
Console.WriteLine($"读取的内容: {content}");
}
}
finally
{
// 清理资源
state.FileStream.Close();
}
}
}
5.3 基于事件的异步模式EAP
EAP模式需要后缀为Async的方法,以及一个或多个的事件、事件委托类型和EventArg派生类型,避免了手动处理回调函数等细节工作。在.net framework 2.0引入,不建议新的开发使用此模式。
/// <summary>
/// Event-based Asynchronous Pattern(EAP模式)
/// </summary>
static void Main(string[] args)
{
var asyncObj = new MyAsyncClass();
// 订阅异步操作完成事件
asyncObj.OperationNameCompleted += AsyncObjOperationNameCompleted;
// 启动异步操作
asyncObj.DoWorkAsync(10);
Console.ReadLine();
}
/// <summary>
/// 异步操作完成事件的处理方法
/// </summary>
/// <param name="result">result</param>
private static void AsyncObjOperationNameCompleted(int result)
{
Console.WriteLine($"异步操作完成,结果为: {result}");
}
public class MyAsyncClass : Component
{
/// <summary>
/// 声明一个委托类型,用于定义异步操作的方法签名
/// </summary>
/// <param name="arg"></param>
/// <returns></returns>
public delegate void MyAsyncDelegate(int arg);
/// <summary>
/// 声明一个事件,用于通知异步操作的完成
/// </summary>
public event MyAsyncDelegate OperationNameCompleted;
/// <summary>
/// 异步执行方法,接受一个参数 arg
/// </summary>
/// <param name="arg"></param>
public void DoWorkAsync(int arg)
{
// 将异步操作放入线程池中执行
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), arg);
}
/// <summary>
/// 真正的异步操作
/// </summary>
/// <param name="obj"></param>
private void DoWork(object obj)
{
int arg = (int)obj;
int res = arg + 1;
// 触发事件,传递异步操作的结果
OperationNameCompleted?.Invoke(res);
}
}
6、实现线程同步
在多线程编程中,线程同步是确保多个线程能够正确、有序地访问共享资源的关键机制。如果没有适当的同步机制,可能会导致数据竞争、死锁、资源争用等问题。
6.1 锁(Lock)
锁是最常用的线程同步机制,通过限制同一时间只有一个线程访问共享资源来实现同步。
- lock 关键字(C#)
lock 是 C# 中最简单的锁机制,基于 Monitor 类实现。简单易用,适用于临界区较小的场景。
private static readonly object _lock = new object();
private static int _counter = 0;
public static void IncrementCounter()
{
lock (_lock)
{
_counter++;
}
}
- Monitor 类
Monitor 是 lock 关键字的基础,提供了更灵活的控制(如超时机制),比 lock 更灵活。
private static readonly object _lock = new object();
public static void IncrementCounter()
{
bool lockTaken = false;
try
{
Monitor.Enter(_lock, ref lockTaken);
_counter++;
}
finally
{
if (lockTaken)
{
Monitor.Exit(_lock);
}
}
}
6.2 信号量(Semaphore)
Semaphore 是一个计数信号量,可以限制同时访问资源的线程数量(如数据库连接池、线程池)。支持跨进程同步。
private static Semaphore _semaphore = new Semaphore(initialCount: 2, maximumCount: 2);
public static void AccessResource()
{
_semaphore.WaitOne(); // 请求信号量
try
{
// 访问共享资源
}
finally
{
_semaphore.Release(); // 释放信号量
}
}
6.3 互斥体(Mutex)
Mutex 是一个系统级别的锁,可以跨进程使用(如多个应用程序访问共享资源)。比 lock 更重量级,性能较低。
查看代码
private static Mutex _mutex = new Mutex();
public static void AccessResource()
{
_mutex.WaitOne(); // 请求互斥体
try
{
// 访问共享资源
}
finally
{
_mutex.ReleaseMutex(); // 释放互斥体
}
}
示例
static void Main(string[] args)
{
bool createdNew;
System.Threading.Mutex instance = new System.Threading.Mutex(true, "MutexName", out createdNew);
if (!createdNew) //已经有应用程序副本执行
{
Application.ExitThread();
return;
}
}
6.4 读写锁(ReaderWriterLock)
读写锁允许多个线程同时读取共享资源,但写操作需要独占访问。适用于读多写少的场景(如缓存、配置管理),需要提高读操作的并发性能。
ReaderWriterLockSlim 是轻量级的读写锁,性能优于 ReaderWriterLock。
private static ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
private static int _sharedData = 0;
public static int ReadData()
{
_rwLock.EnterReadLock();
try
{
return _sharedData;
}
finally
{
_rwLock.ExitReadLock();
}
}
public static void WriteData(int value)
{
_rwLock.EnterWriteLock();
try
{
_sharedData = value;
}
finally
{
_rwLock.ExitWriteLock();
}
}
6.5 事件(Event)
事件用于线程间的通知机制,允许一个线程等待另一个线程的信号。
- ManualResetEvent
需要手动重置事件状态,每次 Set 只能释放一个等待的线程。适合一对多的场景。
查看代码
using System;
using System.Threading;
class Program
{
private static AutoResetEvent _event = new AutoResetEvent(initialState: false);
static void Main()
{
Thread workerThread = new Thread(DoWork);
workerThread.Start();
Console.WriteLine("Main thread is waiting for a signal.");
_event.WaitOne(); // 等待信号
Console.WriteLine("Main thread received the signal.");
workerThread.Join();
}
static void DoWork()
{
Console.WriteLine("Worker thread is doing some work.");
Thread.Sleep(2000); // 模拟耗时操作
Console.WriteLine("Worker thread is sending a signal.");
_event.Set(); // 发送信号
}
}
/*
Main thread is waiting for a signal.
Worker thread is doing some work.
Worker thread is sending a signal.
Main thread received the signal.*/
- AutoResetEvent
AutoResetEvent自动重置事件状态,每次 Set 会释放所有等待的线程。适合一对一的场景。ManualResetEventSlim 是 ManualResetEvent 的轻量级版本,适用于短时间等待的场景。
查看代码
using System;
using System.Threading;
class Program
{
private static ManualResetEventSlim _event = new ManualResetEventSlim(initialState: false);
static void Main()
{
Thread workerThread = new Thread(DoWork);
workerThread.Start();
Console.WriteLine("Main thread is waiting for a signal.");
_event.Wait(); // 等待信号
Console.WriteLine("Main thread received the signal.");
workerThread.Join();
}
static void DoWork()
{
Console.WriteLine("Worker thread is doing some work.");
Thread.Sleep(2000); // 模拟耗时操作
Console.WriteLine("Worker thread is sending a signal.");
_event.Set(); // 发送信号
}
}
/*
Main thread is waiting for a signal.
Worker thread is doing some work.
Worker thread is sending a signal.
Main thread received the signal.
*/
6.6 屏障(Barrier)
Barrier 允许多个线程在某个点等待,直到所有线程都到达后再继续执行。
private static Barrier _barrier = new Barrier(participantCount: 3);
public static void ThreadWork()
{
Console.WriteLine("Thread reached the barrier.");
_barrier.SignalAndWait(); // 等待其他线程
Console.WriteLine("Thread passed the barrier.");
}
6.7 原子操作(Atomic Operations)
原子操作是不可分割的操作,确保在多线程环境下不会被打断。
Interlocked 提供了一系列原子操作方法,如 Increment、Decrement、Exchange 等。适用于简单的计数器场景,性能高,无需锁。
private static int _counter = 0;
public static void IncrementCounter()
{
Interlocked.Increment(ref _counter);
}
6.8 线程安全的集合
.NET 提供了多种线程安全的集合类,如 ConcurrentQueue、ConcurrentDictionary 等。
ConcurrentQueue 是一个线程安全的队列。适用于多线程环境下的数据共享,无需手动加锁。
private static ConcurrentQueue<int> _queue = new ConcurrentQueue<int>();
public static void EnqueueData(int value)
{
_queue.Enqueue(value);
}
public static bool TryDequeueData(out int value)
{
return _queue.TryDequeue(out value);
}

浙公网安备 33010602011771号