跳转到主内容
极星编程网:以代码为星,赴技术山海!

C# 底层原理概述与代码示范

C# 作为一门现代面向对象编程语言,其底层实现涉及 .NET 运行时、CLR (Common Language Runtime)、JIT 编译、内存管理等核心机制。

下面我将从多个维度深入剖析 C# 的底层原理,并提供相应的代码示范。

一、CLR 与运行时基础 1. CLR 架构概述 CLR 是 .NET 的执行引擎,负责: 管理应用程序执行 内存管理(垃圾回收) 类型安全检查 异常处理 线程管理 ​ ​代码示范:查看程序集信息​

using System;

using System.Reflection;

class Program { static void Main() { Assembly assembly = Assembly.GetExecutingAssembly(); Console.WriteLine($"程序集名称: {assembly.FullName}"); Console.WriteLine($"CLR 版本: {assembly.ImageRuntimeVersion}");

// 查看程序集引用的其他程序集 foreach (var reference in assembly.GetReferencedAssemblies()) { Console.WriteLine($"引用程序集: {reference.Name}"); } } } 2. 类型系统与元数据 C# 是强类型语言,所有类型信息都存储在程序集的元数据中。

​ ​代码示范:反射查看类型信息​

using System;

using System.Reflection;

public class Person { public string Name { get; set; } public int Age { get; set; } }

class Program { static void Main() { Type personType = typeof(Person);

Console.WriteLine($"类型名称: {personType.Name}"); Console.WriteLine($"命名空间: {personType.Namespace}"); Console.WriteLine($"基类: {personType.BaseType?.Name}");

// 查看属性 Console.WriteLine("\n属性:"); foreach (var prop in personType.GetProperties()) { Console.WriteLine($"- {prop.Name} ({prop.PropertyType.Name})"); } } } 二、内存管理与垃圾回收 1. 托管堆与垃圾回收 CLR 使用托管堆来分配对象,垃圾回收器(GC)自动管理内存。

​ ​代码示范:观察垃圾回收行为​

using System;

class Program { static void Main() { // 创建大量对象观察GC行为 for (int i = 0; i < 100000; i++) { var obj = new LargeObject(); if (i % 10000 == 0) { Console.WriteLine($"已创建 {i} 个对象"); // 建议GC运行(只是建议,不保证立即执行) GC.Collect(); GC.WaitForPendingFinalizers(); } } } }

class LargeObject { private byte[] data = new byte[1024]; // 1KB数据 } 2. 代际垃圾回收 CLR 使用分代垃圾回收策略: 第0代:新分配的对象 第1代:经历过一次GC的对象 第2代:经历过多次GC的对象 ​ ​代码示范:强制不同代的GC​

using System;

class Program { static void Main() { // 第0代对象 var gen0Obj = new object();

// 强制第0代GC GC.Collect(0, GCCollectionMode.Forced);

// 第1代对象(通过保持引用) var gen1Obj = new object(); var tempRef = gen1Obj; // 保持引用使对象晋升到第1代

// 强制第1代GC GC.Collect(1, GCCollectionMode.Forced);

// 第2代对象 var gen2Obj = new object(); // 保持引用使对象晋升到第2代 var longLivedRef = gen2Obj;

// 强制第2代GC GC.Collect(2, GCCollectionMode.Forced); } } 三、JIT 编译与执行 1. JIT 编译过程 C# 代码先编译为 IL (Intermediate Language),然后在运行时由 JIT 编译器编译为本地机器码。

​ ​代码示范:查看IL代码​

// 使用ILDasm工具查看IL代码

// 或使用以下代码获取方法信息 using System; using System.Reflection;

public class Calculator { public int Add(int a, int b) => a + b; }

class Program { static void Main() { MethodInfo method = typeof(Calculator).GetMethod("Add"); Console.WriteLine($"方法名称: {method.Name}"); Console.WriteLine($"返回类型: {method.ReturnType.Name}");

// 参数信息 foreach (var param in method.GetParameters()) { Console.WriteLine($"参数: {param.Name} ({param.ParameterType.Name})"); } } } 2. 方法内联优化 JIT 编译器会进行方法内联等优化。

​ ​代码示范:观察方法调用开销​

using System;

using System.Diagnostics;

class Program { static void Main() { const int iterations = 100000000;

// 直接计算 var sw = Stopwatch.StartNew(); int directResult = 0; for (int i = 0; i < iterations; i++) { directResult += i + i; // 简单计算 } sw.Stop(); Console.WriteLine($"直接计算耗时: {sw.ElapsedMilliseconds}ms");

// 方法调用 sw.Restart(); int methodResult = 0; for (int i = 0; i < iterations; i++) { methodResult += Add(i, i); // 方法调用 } sw.Stop(); Console.WriteLine($"方法调用耗时: {sw.ElapsedMilliseconds}ms");

// JIT可能会内联Add方法,使两者性能接近 }

static int Add(int a, int b) => a + b; } 四、委托与事件底层原理 1. 委托的本质 委托是类型安全的函数指针。

​ ​代码示范:反射查看委托信息​

using System;

using System.Reflection;

public delegate void MyDelegate(string message);

class Program { static void Main() { // 创建委托实例 MyDelegate del = ShowMessage;

// 反射查看委托类型信息 Type delegateType = typeof(MyDelegate); Console.WriteLine($"委托名称: {delegateType.Name}"); Console.WriteLine($"基础类型: {delegateType.BaseType?.Name}");

// 获取Invoke方法(委托调用的核心) MethodInfo invokeMethod = delegateType.GetMethod("Invoke"); Console.WriteLine($"Invoke方法参数: {invokeMethod.GetParameters().Length}个"); }

static void ShowMessage(string msg) { Console.WriteLine(msg); } } 2. 事件实现原理 事件本质上是委托的封装。

​ ​代码示范:自定义事件实现​

using System;

// 自定义事件实现 public class CustomEvent { // 委托类型 public delegate void EventHandler(string message);

// 事件字段 private event EventHandler _event;

// 公开的事件属性 public event EventHandler Event { add { _event += value; } remove { _event -= value; } }

// 触发事件方法 public void RaiseEvent(string message) { _event?.Invoke(message); } }

class Program { static void Main() { var customEvent = new CustomEvent();

// 订阅事件 customEvent.Event += (msg) => Console.WriteLine($"收到消息: {msg}");

// 触发事件 customEvent.RaiseEvent("Hello, Event!"); } } 五、LINQ 底层原理 1. LINQ to Objects 实现 LINQ 查询会被编译为方法调用。

​ ​代码示范:查看LINQ查询生成的IL​

using System;

using System.Collections.Generic; using System.Linq;

class Program { static void Main() { var numbers = new List { 1, 2, 3, 4, 5 };

// LINQ查询语法 var querySyntax = from n in numbers where n % 2 == 0 select n * 2;

// LINQ方法语法 var methodSyntax = numbers.Where(n => n % 2 == 0).Select(n => n * 2);

// 两种语法生成的IL几乎相同 Console.WriteLine("查询结果:"); foreach (var item in querySyntax) { Console.WriteLine(item); } } } 2. 延迟执行原理 LINQ 查询默认是延迟执行的。

​ ​代码示范:观察延迟执行行为​

using System;

using System.Collections.Generic; using System.Linq;

class Program { static void Main() { var numbers = new List { 1, 2, 3, 4, 5 };

// 创建查询(此时不执行) var query = numbers.Where(n => { Console.WriteLine($"处理数字: {n}"); return n % 2 == 0; });

Console.WriteLine("查询创建完成,但尚未执行");

// 实际枚举时才执行 Console.WriteLine("开始枚举结果:"); foreach (var item in query) { Console.WriteLine(item); }

// 再次枚举会再次执行查询 Console.WriteLine("再次枚举结果:"); foreach (var item in query) { Console.WriteLine(item); } } } 六、异步编程底层原理 1. async/await 状态机 async 方法会被编译器转换为状态机。

​ ​代码示范:查看async方法生成的IL​

using System;

using System.Threading.Tasks;

class Program { static async Task Main() { await SampleAsyncMethod(); }

static async Task SampleAsyncMethod() { Console.WriteLine("开始异步方法"); await Task.Delay(100); Console.WriteLine("异步方法完成"); } } 2. 任务调度原理 Task 使用线程池进行调度。

​ ​代码示范:观察任务调度行为​

using System;

using System.Threading; using System.Threading.Tasks;

class Program { static void Main() { Console.WriteLine($"主线程ID: {Thread.CurrentThread.ManagedThreadId}");

Task.Run(() => { Console.WriteLine($"任务1线程ID: {Thread.CurrentThread.ManagedThreadId}"); });

Task.Run(() => { Console.WriteLine($"任务2线程ID: {Thread.CurrentThread.ManagedThreadId}"); });

// 等待任务完成 Task.WaitAll(Task.Delay(100)); } } 七、反射与动态代码生成 1. 反射性能考虑 反射操作比直接调用慢,但提供了灵活性。

​ ​代码示范:反射性能测试​

using System;

using System.Diagnostics; using System.Reflection;

class Program { static void Main() { var method = typeof(Program).GetMethod("DirectMethod");

// 直接调用 var sw = Stopwatch.StartNew(); for (int i = 0; i < 1000000; i++) { DirectMethod(); } sw.Stop(); Console.WriteLine($"直接调用耗时: {sw.ElapsedMilliseconds}ms");

// 反射调用 sw.Restart(); for (int i = 0; i < 1000000; i++) { method.Invoke(null, null); } sw.Stop(); Console.WriteLine($"反射调用耗时: {sw.ElapsedMilliseconds}ms"); }

static void DirectMethod() { } } 2. 动态代码生成 使用 Expression Tree 或 DynamicMethod 生成动态代码。

​ ​代码示范:使用Expression Tree创建动态方法​

using System;

using System.Linq.Expressions;

class Program { static void Main() { // 创建参数表达式 ParameterExpression paramA = Expression.Parameter(typeof(int), "a"); ParameterExpression paramB = Expression.Parameter(typeof(int), "b");

// 创建加法表达式 BinaryExpression add = Expression.Add(paramA, paramB);

// 创建lambda表达式 var lambda = Expression.Lambda>(add, paramA, paramB);

// 编译为委托 Func addDelegate = lambda.Compile();

// 调用动态生成的方法 int result = addDelegate(3, 5); Console.WriteLine($"3 + 5 = {result}"); } } 总结 C# 的底层原理涉及多个层次: CLR 提供运行时环境 JIT 编译将 IL 转换为本地代码 内存管理通过垃圾回收器自动处理 委托和事件提供灵活的编程模型 LINQ 提供声明式查询能力 异步编程模型简化并发编程 反射和动态代码生成提供运行时灵活性

相关文章