浅谈.NET中的IL代码

在我们分析查看IL之前首先要了解下什么是IL?IL的全称是Intermediate Language (IL)即将.NET代码转化为机器语言的一个中间语言的缩写。在一定程度上,我们可以将其理解为伪汇编语言。我们在使用.NET框架中的C#、VB.NET、F#等语言的时候,编译过程并不是像C/C++一样直接编译出原生代码,而是编译成IL中间语言。通过IL中间语言这种方式,可以实现跨平台、提高程序灵活性等多种优点。 首先编译器将我们编写好的源代码编译成IL中间语言,这些IL中间语言的主要内容是一些元数据和中间语言指令。然后再由我们的JIT编译器加载这些IL中间语言,JIT编译器会根据系统环境将IL中间语言指令转换为机器码,继而运行在不同的目标平台上,实现跨平台功能。(JIT编译器将IL中间语言即时编译成原生语言的过程和解释性语言的读取一条执行一条又有些不同,JIT会对编译结果进行缓存以便下次调取的时候直接使用)这也是为什么有些ASP.NET网站第一次运行时会较慢,而后面的执行速度则会相对快很多的一个原因。 再总结一下上面所说的编译过程: 首先,编译器要编历源代码,通过大量的计算生成IL中间代码,这些代码并不能直接地被CPU使用,还需要第二步操作; 接下来,运行时将这些IL代码通过JIT编译器进一步编译成原生的CPU指令。 在上文中我们提到了一个JIT编译器,它的全名叫即时编译器。顾名思义,它是在运行时环境中发生的编译行为。读到这里,相信很多朋友可能都会像马三一样产生疑问了。相比传统的直接将源代码编译成原生代码,C#将源代码编译成了中间语言不会降低效率嘛?原来直接一步到位的过程,现在偏要拆成两个部分。这不仅要花费更多的时间、占用更多的内存,还有可能降低性能,那用JIT编译器的好处到底有什么呢? 在我们的Unity游戏开发中就存在着AOT编译和JIT编译两种编译方式 unity公司就自行研发了IL2cpp,把本来应该再mono的虚拟机上跑的中间代码转换成cpp代码,这样再把生成的cpp代码,利用c++的跨平台特性, 在各个平台上通过对各平台都有良好优化的native c++编译器编译,以获得更高的效率和更好的兼容性。 语言在运行之前通常都需要编译,JIT(Just-in-Time,即时编译) 和 AOT(Ahead-of-Time,预编译) 则是最常见的两种编译模式。 JIT 在运行时即时编译,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响。 AOT 即提前编译,可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低。 总结来讲,在开发期使用 JIT 编译,可以缩短产品的开发周期。 那么,如何区分一门语言究竟是 AOT 还是 JIT 呢? 通常来说,看代码在执行前是否需要编译即可。如果需要编译,通常属于 AOT;如果不需要,则属于 JIT。 AOT 的典型代表是 C/C++,它们必须在执行前编译成机器码;而 JIT 的代表,则包括了如 JavaScript、Python 等几乎所有的脚本语言。

2025年7月28日 · 1 分钟 · 38 字

JIT(Just-In-Time)编译器

在 .NET 中,JIT(Just-In-Time)编译器是一种将 MSIL(Microsoft Intermediate Language)代码转换为本机机器代码的动态编译器。 JIT 编译器在程序运行时将 MSIL 代码编译为本机代码,以便在计算机上运行。 JIT 编译器是 .NET 运行时的一部分,它负责将 MSIL 代码转换为本机代码。 当 .NET 应用程序启动时,CLR(Common Language Runtime)会将 MSIL 代码加载到内存中, 然后 JIT 编译器会将代码编译为本机代码,以便在计算机上运行。这个过程是在运行时动态进行的,因此称之为 JIT。 JIT 编译器的好处是它可以根据当前计算机的硬件和操作系统环境来优化代码, 以提高应用程序的性能。它还可以在运行时检查代码中的错误,以避免运行时错误。 JIT 编译器还可以根据代码的执行情况进行优化,以提高应用程序的性能。 总之,JIT 编译器是 .NET 运行时的重要组成部分,它可以将 MSIL 代码转换为本机代码, 以便在计算机上运行,并且可以根据当前环境进行优化,以提高应用程序的性能。

2025年7月28日 · 1 分钟 · 39 字

.NET异步方法

Task.run和threadpool.QueueUserWorkItem Task.Run 和 ThreadPool.QueueUserWorkItem 都是在 .NET 中用于执行异步任务的方法,但它们之间有一些区别。 返回值类型不同:Task.Run 返回一个 Task 对象,而 ThreadPool.QueueUserWorkItem 没有返回值。 参数不同:Task.Run 接受一个 Func<Task> 或 Action 委托,而 ThreadPool.QueueUserWorkItem 接受一个 WaitCallback 委托。 线程池的使用方式不同:Task.Run 会使用线程池中的线程来执行任务,而 ThreadPool.QueueUserWorkItem 只能使用线程池中的线程来执行任务。 异常处理不同:Task.Run 可以通过 await 关键字或 Task.Wait 方法等方式来捕获任务中的异常,而 ThreadPool.QueueUserWorkItem 没有提供类似的机制,需要在任务中自行处理异常。 综上所述,Task.Run 更加灵活,可以更好地支持异步编程模式,而 ThreadPool.QueueUserWorkItem 更适合执行简单的后台任务。在一般情况下,建议使用 Task.Run 来执行异步任务。 在C#中,用于同步线程的机制有以下几种: Monitor:用于同步在同一个进程内的线程。 Mutex:用于同步跨进程的线程。 Semaphore:用于控制同时访问某个资源的线程数量。 AutoResetEvent:用于线程之间的通信,允许一个线程在等待另一个线程信号时暂停执行。 ManualResetEvent:与AutoResetEvent类似,但允许多个线程同时等待信号。 CountdownEvent:允许一个线程等待多个线程完成。 Barrier:用于同步多个线程,允许线程在达到某个点时暂停执行。 ReaderWriterLock:允许多个线程同时读取某个资源,但只允许一个线程写入。 SpinLock:用于在高并发情况下保护共享资源。 以上这些同步机制都有其特定的使用场景和优缺点,需要根据具体的需求来选择适合的同步机制。 Invoke与BeginInvoke的不同 不同点: Invoke是同步更新,会阻塞所在工作者线程,BeginInvoke是异步更新,不会阻塞当前线程。 Invoke调用后将指定代码立即插入主线程中执行,而BeginInvoke调用后,发送消息给UI线程, 相当于向Dispatcher队列中添加工作项,待之前UI更新任务完成后,再执行BeginInvoke中的内容。 在使用场景上,Invoke方法会拖延线程直到执行完指定代码,如果需要暂停异步操作,直到用户提供反馈信息,可使用Invoke, 比如指定代码弹出YES/NO对话框,需要根据用户反馈以进一步执行操作的场景。 相同点:两者虽然一个为同步,一个为异步,实际是运行在同一线程,即UI主线程。 常用的委托类型 Action:表示一个不返回值、不带参数的委托。 Func:表示一个带有一个返回值、不带参数的委托。 Predicate:表示一个带有一个返回值、一个参数的委托。 EventHandler:表示一个用于处理事件的委托,它带有两个参数,第一个参数是事件源对象,第二个参数是事件参数。 EventHandler:表示一个用于处理事件的委托,它带有两个参数,第一个参数是事件源对象,第二个参数是事件参数,其中事件参数类型为 TEventArgs。 Action:表示一个不返回值、带有一个参数的委托,参数类型为 T。 Action<T1, T2>:表示一个不返回值、带有两个参数的委托,参数类型分别为 T1 和 T2。 Action<T1, T2, T3>:表示一个不返回值、带有三个参数的委托,参数类型分别为 T1、T2 和 T3。 Func:表示一个带有一个返回值、不带参数的委托,返回值类型为 T。 Func<T, TResult>:表示一个带有一个参数、一个返回值的委托,参数类型为 T,返回值类型为 TResult。 Func<T1, T2, TResult>:表示一个带有两个参数、一个返回值的委托,参数类型分别为 T1 和 T2,返回值类型为 TResult。 Func<T1, T2, T3, TResult>:表示一个带有三个参数、一个返回值的委托,参数类型分别为 T1、T2 和 T3,返回值类型为 TResult。 这些委托类型都是 .NET Framework 中预定义的常用委托类型,你也可以自定义委托类型来满足特定的需求。

2025年7月28日 · 1 分钟 · 105 字

.NET Framework 自托管详解

.NET Framework 自托管详解 自托管(Self-Hosting)是指不依赖 IIS 等外部 Web 服务器,而是通过应用程序自身创建和管理 Web 服务器实例的方式运行 Web 应用程序。 1. HttpListener 方式 这是最基础的自托管方式,使用 .NET Framework 内置的 HttpListener 类: using System; using System.IO; using System.Net; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // 创建 HttpListener 实例 HttpListener listener = new HttpListener(); // 添加监听的 URL 前缀 listener.Prefixes.Add("http://localhost:8080/"); // 启动监听 listener.Start(); Console.WriteLine("服务器启动,监听 http://localhost:8080/"); // 处理请求循环 while (true) { // 等待请求 HttpListenerContext context = await listener.GetContextAsync(); // 处理请求 await ProcessRequest(context); } } static async Task ProcessRequest(HttpListenerContext context) { HttpListenerResponse response = context.Response; // 设置响应内容 string responseString = "<html><body><h1>Hello from Self-Hosted Server!</h1></body></html>"; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); // 设置响应头 response.ContentType = "text/html"; response.ContentLength64 = buffer.Length; // 写入响应 using (Stream output = response.OutputStream) { await output.WriteAsync(buffer, 0, buffer.Length); } // 关闭响应 response.Close(); } } 2. ASP.NET Web API 自托管 使用 Microsoft.AspNet.WebApi.SelfHost 包可以创建更完整的 Web API 服务: ...

2025年7月24日 · 2 分钟 · 401 字

winform程序防止多开

Winform启动的时候,检测是否存在同样的进程名,防止程序多开; static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { string processName = Process.GetCurrentProcess().ProcessName; Process[] processes = Process.GetProcessesByName(processName); //如果该数组长度大于1,说明多次运行 if (processes.Length > 1) { MessageBox.Show("程序已运行,不能再次打开!"); Environment.Exit(1); } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } 利用Mutex互斥对象防止程序多开; static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { bool isAppRunning = false; Mutex mutex = new Mutex(true, System.Diagnostics.Process.GetCurrentProcess().ProcessName, out isAppRunning); if (!isAppRunning) { MessageBox.Show("程序已运行,不能再次打开!"); Environment.Exit(1); } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }

2025年7月24日 · 1 分钟 · 82 字

.NET的版本

.NET Framework 是微软在 2002 年推出的一个软件开发框架,它提供了一组类库和运行时环境,可以用来开发 Windows 应用程序、Web 应用程序、服务端应用程序等。下面是 .NET Framework 的版本列表: .NET Framework 1.0 .NET Framework 1.1 .NET Framework 2.0 .NET Framework 3.0 .NET Framework 3.5 .NET Framework 4.0 .NET Framework 4.5 .NET Framework 4.5.1 .NET Framework 4.5.2 .NET Framework 4.6 .NET Framework 4.6.1 .NET Framework 4.6.2 .NET Framework 4.7 .NET Framework 4.7.1 .NET Framework 4.7.2 .NET Framework 4.8 除了 .NET Framework,微软还推出了 .NET Core 和 .NET 5,它们是跨平台的 .NET 实现,可以在 Windows、Linux 和 macOS 上运行。下面是 .NET Core 和 .NET 5 的版本列表: .NET Core 1.0 .NET Core 1.1 .NET Core 2.0 .NET Core 2.1 .NET Core 2.2 .NET Core 3.0 .NET Core 3.1 .NET 5.0 需要注意的是,.NET Framework 和 .NET Core 是两个不同的产品,它们有不同的类库和运行时环境,并且不完全兼容。同时,.NET 5 是 .NET Core 的后继版本,它将 .NET Framework、.NET Core 和 Xamarin 合并为一个平台。

2025年7月24日 · 1 分钟 · 119 字

什么是IntPtr

IntPtr是一种特殊的数据类型,它通常用于在编程语言中表示指针或句柄的整数值。“IntPtr"是"Integer Pointer"的缩写。 在许多编程语言中,指针是一种变量,它存储了内存地址的值,可以用来访问内存中的数据。然而,某些编程语言不直接支持指针,或者在特定的上下文中需要使用整数值来表示指针。这时,可以使用IntPtr类型来存储指针或句柄的整数值。 IntPtr类型的大小和表示方式取决于所使用的编程语言和平台。在一些编程语言中,IntPtr类型的大小与指针的大小相同,通常是与机器的字长相匹配的大小。在其他编程语言中,IntPtr类型可能会有固定的大小,无论平台的字长如何。 使用IntPtr类型时需要小心,因为它涉及到直接操作内存地址,可能引发安全和稳定性问题。在使用IntPtr类型时,应该遵循编程语言和平台的相关规范,并且谨慎处理指针操作,以避免潜在的错误和漏洞。 以下是一个示例,展示了如何使用 IntPtr 来处理指针操作: using System; class Program { static void Main() { // 创建一个 IntPtr 对象 IntPtr ptr; // 分配内存并获取指针 int size = sizeof(int); ptr = Marshal.AllocHGlobal(size); // 将值存储到指针指向的内存位置 int value = 42; Marshal.WriteInt32(ptr, value); // 从指针指向的内存位置读取值 int readValue = Marshal.ReadInt32(ptr); Console.WriteLine("Value read from pointer: " + readValue); // 释放分配的内存 Marshal.FreeHGlobal(ptr); // 等待用户输入,防止控制台窗口关闭 Console.ReadLine(); } } 在这个示例中,我们首先创建了一个 IntPtr 对象 ptr,然后使用 Marshal.AllocHGlobal 方法分配了一块内存,并将其指针存储在 ptr 中。然后,我们使用 Marshal.WriteInt32 方法将一个整数值存储到指针指向的内存位置。接下来,我们使用 Marshal.ReadInt32 方法从指针指向的内存位置读取值,并将其打印到控制台上。最后,我们使用 Marshal.FreeHGlobal 方法释放了分配的内存。 请注意,使用指针操作时需要小心,确保正确处理内存分配和释放,以避免内存泄漏和潜在的安全问题。

2025年7月23日 · 1 分钟 · 76 字

DllImport特性

在C#中,DllImport是一种特性,它被用于指定在本地代码中调用的DLL(动态链接库)的入口点。DllImport特性通常被应用于C#中的托管代码中,以便调用由C、C++或者其他非托管语言编写的DLL。 使用DllImport特性,你可以在C#中调用DLL中的函数,而不需要了解该DLL的内部实现细节。这使得C#能够与各种不同的外部库进行交互,并扩展了C#的功能。 DllImport特性的语法如下: [DllImport("dllname.dll", CallingConvention = CallingConvention.Cdecl)] public static extern returnType functionName(parameterList); 其中: “dllname.dll” 是包含目标函数的DLL的名称。 CallingConvention.Cdecl 是调用约定,指定了函数如何传递参数和返回值。 returnType 是函数的返回类型。 functionName 是要调用的函数的名称。 parameterList 是函数的参数列表。 需要注意的是,DllImport特性只能用于托管代码,即由公共语言运行时(CLR)管理的代码。对于非托管代码,如直接调用操作系统API或硬件接口等,需要使用P/Invoke(平台调用服务)来实现。

2025年7月22日 · 1 分钟 · 24 字