C/C++编译移植与C#跨平台实现:核心技术差异与实战选型指南

📅2026/7/14 16:26:55 👁️次浏览
C/C++编译移植与C#跨平台实现:核心技术差异与实战选型指南
1. 项目概述为什么我们需要深入理解C/C与C#的跨平台差异在软件开发领域“跨平台”是一个既令人兴奋又充满挑战的词汇。对于C、C和C#这三种主流语言它们的跨平台能力实现路径截然不同背后折射出的是语言设计哲学、运行时环境以及生态体系的根本差异。很多开发者尤其是从单一平台入门的程序员常常会混淆这些概念导致在技术选型或项目迁移时踩坑。比如有人以为用C#写的桌面应用天然就能在Linux上运行或者认为C的跨平台就是简单地换个编译器重新编译。实际上C/C的“编译移植”和C#的“跨平台实现”是两套完全不同的技术体系。简单来说C和C的跨平台核心在于源码级兼容和编译器工具链。你写一份遵循特定标准如C11、C17和可移植性实践的源代码然后针对不同的目标平台x86 Linux, ARM macOS, Windows等使用对应的编译器GCC, Clang, MSVC进行编译生成该平台原生的二进制可执行文件。这个过程我们通常称为“移植”Porting。它的优势是性能极致直接操作硬件但代价是需要开发者手动管理不同平台间的系统API差异、库依赖和编译配置复杂度高。而C#的跨平台其核心在于运行时环境即.NET运行时如.NET Framework, .NET Core/.NET 5。C#代码被编译成一种中间语言IL然后在装有相应.NET运行时的任何操作系统上由即时编译器JIT或提前编译器AOT转换成本地代码执行。.NET Core及之后的统一平台.NET 5/6/7/8通过提供一套标准的基础类库BCL和平台抽象层使得同一份IL代码可以在Windows、Linux、macOS上运行。这个过程更侧重于“一次编写到处运行”但性能开销和运行时依赖是其需要考虑的因素。理解这两种路径不仅能帮助你在面对“将Windows服务迁移到Linux”或“开发一个全平台可用的桌面工具”时做出正确的技术决策更能让你深入理解系统软件与应用软件、原生性能与开发效率之间的经典权衡。接下来我将结合十多年的实战经验为你层层拆解这两种方案的核心技术细节、实操步骤以及那些官方文档里不会写的“坑”。2. C/C的编译移植从源码到多平台二进制C/C的跨平台能力本质上是一场开发者与操作系统、硬件架构之间的“约定”。语言标准如ISO C/C定义了语言的语法和核心库但文件系统、网络、线程、图形界面等都属于平台特定的领域。因此C/C的跨平台不是魔法而是一系列严谨的工程实践。2.1 核心思想条件编译与抽象层实现C/C跨平台的第一原则是隔离平台相关代码。你不能在Linux代码里直接调用Win32 API也不能在Windows代码里直接使用POSIX线程函数。常见的做法有两种条件编译#ifdef这是最直接但也最容易导致代码混乱的方法。通过在代码中插入预处理器指令为不同平台编写不同的代码块。#ifdef _WIN32 #include windows.h Sleep(1000); // Windows下的毫秒级休眠 #elif defined(__linux__) #include unistd.h sleep(1); // Linux下的秒级休眠 // 注意Linux的sleep单位是秒如需毫秒需用usleep或nanosleep #elif defined(__APPLE__) #include unistd.h sleep(1); // macOS同样 #endif注意事项过度使用#ifdef会使代码难以阅读和维护。通常只用于非常细粒度的、无法通过抽象层隔离的平台差异。抽象层Abstraction Layer这是更优雅和可维护的做法。为平台相关的功能如线程、文件IO、网络套接字定义一个统一的接口头文件然后为每个平台提供不同的实现.c/.cpp文件。接口platform_thread.h:typedef void* thread_handle_t; thread_handle_t create_thread(void (*func)(void*), void* arg); void join_thread(thread_handle_t thread);Windows实现platform_thread_win.c:#include windows.h thread_handle_t create_thread(void (*func)(void*), void* arg) { return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, NULL); }Linux实现platform_thread_linux.c:#include pthread.h thread_handle_t create_thread(void (*func)(void*), void* arg) { pthread_t thread; pthread_create(thread, NULL, (void*(*)(void*))func, arg); return (thread_handle_t)thread; }在编译时通过构建系统如CMake选择链接对应平台的实现文件。大型开源项目如Chromium、MySQL都采用这种架构。2.2 工具链选型与构建系统选择正确的编译器和构建系统是成功的一半。编译器GCC (GNU Compiler Collection)和Clang是Linux、macOS及许多嵌入式平台上的主流选择对C/C标准支持好跨平台编译能力强。MSVC (Microsoft Visual C)Windows平台的官方编译器与Visual Studio深度集成。对于需要紧密依赖Windows特定功能的项目不可或缺。交叉编译器Cross-Compiler当你需要在A平台如x86_64 Linux上编译生成能在B平台如ARM Linux上运行的二进制文件时就需要交叉编译器如aarch64-linux-gnu-gcc。构建系统手动写Makefile管理多平台编译是噩梦。现代C/C项目强烈推荐使用CMake。为什么是CMake它是一个元构建系统可以生成针对不同平台和编译器的原生构建文件如Linux的Makefile、Windows的Visual Studio .sln、macOS的Xcode项目。一个简单的跨平台CMakeLists.txt示例cmake_minimum_required(VERSION 3.10) project(MyCrossPlatformApp) # 设置C标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 根据平台定义宏或链接库 if(WIN32) add_definitions(-DPLATFORM_WINDOWS) # 在Windows上链接Ws2_32库用于Socket target_link_libraries(MyApp PRIVATE Ws2_32) elseif(UNIX AND NOT APPLE) add_definitions(-DPLATFORM_LINUX) # 在Linux上链接pthread库 target_link_libraries(MyApp PRIVATE pthread) elseif(APPLE) add_definitions(-DPLATFORM_MACOS) # macOS可能需要特定的框架如CoreFoundation这里仅为示例 # target_link_libraries(MyApp PRIVATE -framework CoreFoundation) endif() # 添加可执行文件目标 add_executable(MyApp main.cpp platform_impl.cpp) # 包含头文件目录 target_include_directories(MyApp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)实操心得在项目根目录创建build文件夹并在其中运行cmake ..是一个好习惯这能将生成的文件与源代码分离方便清理。对于交叉编译你需要通过-DCMAKE_TOOLCHAIN_FILE参数指定一个工具链文件Toolchain File其中定义了交叉编译器的路径、目标系统等信息。2.3 第三方库的依赖管理C/C项目几乎不可能从零开始。使用第三方库如JSON解析的nlohmann/json网络库的libcurl时它们的跨平台支持是关键。库的获取方式系统包管理器Linux上用apt-get或yummacOS用HomebrewWindows用vcpkg或MSYS2的pacman。这是最方便的方式但可能版本较旧。源码编译下载库的源代码用CMake或库自带的构建系统如./configure make进行编译。这是最可控的方式可以确保编译选项与你的主项目一致但过程可能复杂。包管理器推荐vcpkg (Microsoft)一个跨平台的C库管理器能自动从源码编译并安装库生成供CMake或Visual Studio使用的配置文件。命令如vcpkg install curl:x64-windows或vcpkg install curl:x64-linux。Conan一个去中心化的C/C包管理器功能更强大支持二进制包的下载和缓存能更好地管理复杂的依赖图和版本冲突。在CMake中集成vcpkg首先安装vcpkg然后设置CMAKE_TOOLCHAIN_FILE环境变量指向vcpkg的scripts/buildsystems/vcpkg.cmake文件。在你的CMakeLists.txt中使用find_package(CURL REQUIRED)CMake就会自动找到vcpkg安装的CURL库。踩坑记录确保vcpkg安装的库的** triplet**如x64-windows-static与你的项目目标动态链接/静态链接匹配。静态链接-static可以避免目标机器上缺少DLL的问题但会增大最终二进制文件体积。3. C#的跨平台实现.NET统一平台的进击之路C#的跨平台故事是.NET生态系统演进的核心脉络。从早期的Windows独占.NET Framework到开源跨平台的.NET Core再到如今统一的.NET 5/6/7/8其跨平台能力已非常成熟和可靠。3.1 .NET运行时跨平台的基石C#代码.cs文件首先被C#编译器csc或dotnet build编译成中间语言IL存储于.dll或.exe程序集中。这个IL代码是平台无关的。当程序运行时.NET运行时Runtime中的即时编译器JIT会将IL代码实时编译成当前CPU架构x86, ARM等的本地机器码执行。这就是“一次编写到处运行”的基础。.NET Framework仅限Windows。它是旧时代的王者但已停止主要功能更新。.NET Core / .NET 5这是跨平台的未来。.NET Core 3.1是第一个支持Windows桌面应用WPF/WinForms跨平台的LTS版本。.NET 5及后续版本.NET 6, 7, 8是真正的统一平台覆盖了云、桌面、移动、物联网等所有场景。Mono一个早期的、社区驱动的.NET运行时开源实现在.NET Core成熟之前是C#在Linux/macOS上运行的主要选择现在其部分技术已融入.NET。关键选择对于全新的跨平台项目无脑选择最新的.NET LTS版本如.NET 8。它拥有最好的性能、最全的API支持和最长的维护周期。3.2 项目类型与跨平台UI框架C#的跨平台性在不同类型的项目上体现不同控制台应用 / 类库 / Web API (ASP.NET Core)这是.NET Core/.NET 5的“舒适区”。创建一个控制台应用你的代码几乎可以在任何支持.NET的平台上无缝运行因为不涉及图形用户界面GUI。这是最常见的跨平台后端服务开发模式。创建命令dotnet new console -n MyCrossPlatformService发布为独立部署dotnet publish -c Release -r linux-x64 --self-contained true这会生成一个包含.NET运行时在内的、可在指定Linux系统上直接运行的独立包。桌面GUI应用这是挑战最大的部分因为UI框架历来与操作系统深度绑定。Avalonia UI目前最受推崇的、真正的跨平台.NET UI框架。它使用XAML描述UI渲染后端支持DirectX、Skia等能在Windows、Linux、macOS上提供接近原生的体验。它的理念和API与WPF非常相似WPF开发者可以较低成本迁移。MAUI (.NET Multi-platform App UI)微软官方的跨平台框架用于开发可运行在Android、iOS、macOS和Windows上的移动端和桌面端应用。对于纯桌面场景其成熟度和社区生态目前略逊于Avalonia但它是微软的亲儿子与Visual Studio集成度极高。Electron.NET / Blazor Hybrid这类方案将Web技术HTML/CSS/JS与.NET后端结合用WebView作为渲染引擎。好处是前端技术栈统一但会带来较大的应用体积和性能开销适合对性能不敏感的内部工具或管理后台。实操建议如果你要开发一个需要复杂UI交互、高性能渲染的跨平台桌面应用Avalonia UI是目前最稳妥的选择。它的社区活跃控件库丰富且有大型商业项目背书。3.3 处理平台特定代码即使是在.NET的抽象之下有时仍需要调用特定操作系统的原生功能如读取Linux的/proc文件系统或调用一个仅Windows才有的系统API。.NET提供了几种机制运行时检查RuntimeInformationusing System.Runtime.InteropServices; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { // 执行Linux特定的逻辑 string cpuInfo File.ReadAllText(/proc/cpuinfo); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // 执行Windows特定的逻辑 var version Environment.OSVersion.Version; }平台调用服务P/Invoke这是从.NET调用非托管C/C DLL在Linux上是.so文件的标准方式。using System.Runtime.InteropServices; // 在Linux上调用libc的getpid函数 public class PlatformInterop { [DllImport(libc, SetLastError true)] public static extern int getpid(); // 在Windows上调用Kernel32的GetCurrentProcessId [DllImport(kernel32.dll, SetLastError true)] public static extern uint GetCurrentProcessId(); } // 使用时需要结合运行时检查 public static int GetProcessId() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return PlatformInterop.getpid(); else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return (int)PlatformInterop.GetCurrentProcessId(); else throw new PlatformNotSupportedException(); }注意事项P/Invoke需要精确匹配原生函数的调用约定、字符集CharSet和结构体布局StructLayout否则会导致程序崩溃或内存错误。务必查阅操作系统的官方文档。条件编译符号你可以在项目文件.csproj中定义条件编译符号或者在代码中使用#if。在.csproj中定义PropertyGroup DefineConstants Condition$(OS) Windows_NTWINDOWS/DefineConstants DefineConstants Condition$(OS) ! Windows_NTLINUX/DefineConstants /PropertyGroup在代码中使用#if WINDOWS // Windows特定代码 Console.WriteLine(Running on Windows.); #elif LINUX // Linux特定代码 Console.WriteLine(Running on Linux.); #endif这种方法在编译时就已经确定了代码路径但会导致同一个程序集在不同平台上行为可能不同如果包含了所有平台的代码。更常见的做法是将平台特定的实现放在不同的类库项目中通过条件引用来选择。4. 实战对比将一个简单工具移植到Windows和Linux让我们通过一个具体的例子来感受两种路径的差异。假设我们要开发一个简单的系统信息查看工具功能是打印当前进程ID和系统时间。4.1 C版本实现使用CMake管理项目结构cpp_sysinfo/ ├── CMakeLists.txt ├── include/ │ └── platform_info.h ├── src/ │ ├── main.cpp │ ├── platform_info_linux.cpp │ └── platform_info_win.cpp └── build/ (后续生成)头文件include/platform_info.h定义跨平台接口。#pragma once #include string namespace PlatformInfo { // 获取当前进程ID int GetCurrentProcessId(); // 获取系统启动后的运行时间毫秒 long long GetSystemUptime(); // 获取平台名称 std::string GetPlatformName(); }Linux实现src/platform_info_linux.cpp#include platform_info.h #include unistd.h #include sys/sysinfo.h #include fstream int PlatformInfo::GetCurrentProcessId() { return static_castint(getpid()); } long long PlatformInfo::GetSystemUptime() { struct sysinfo info; if (sysinfo(info) 0) { return info.uptime * 1000LL; // 秒转毫秒 } return -1; } std::string PlatformInfo::GetPlatformName() { return Linux; }Windows实现src/platform_info_win.cpp#include platform_info.h #include windows.h #include string int PlatformInfo::GetCurrentProcessId() { return static_castint(::GetCurrentProcessId()); } long long PlatformInfo::GetSystemUptime() { return static_castlong long(GetTickCount64()); // Windows启动后的毫秒数 } std::string PlatformInfo::GetPlatformName() { return Windows; }主程序src/main.cpp#include iostream #include platform_info.h int main() { std::cout Platform: PlatformInfo::GetPlatformName() std::endl; std::cout Process ID: PlatformInfo::GetCurrentProcessId() std::endl; std::cout System Uptime (ms): PlatformInfo::GetSystemUptime() std::endl; return 0; }CMakeLists.txt关键是如何选择编译哪个平台文件。cmake_minimum_required(VERSION 3.10) project(SystemInfoTool) set(CMAKE_CXX_STANDARD 17) # 创建库目标先不添加源文件 add_library(platform_info) # 根据平台添加对应的源文件 if(WIN32) target_sources(platform_info PRIVATE src/platform_info_win.cpp) message(STATUS Building for Windows) else() # 默认按Linux处理也可用 elseif(UNIX) target_sources(platform_info PRIVATE src/platform_info_linux.cpp) message(STATUS Building for Linux/Unix) endif() # 包含头文件目录 target_include_directories(platform_info PUBLIC include) # 创建可执行文件并链接我们的库 add_executable(sysinfo_tool src/main.cpp) target_link_libraries(sysinfo_tool PRIVATE platform_info)编译与运行在Linux上:mkdir build cd build cmake .. make ./sysinfo_tool # 输出示例: Platform: Linux \n Process ID: 12345 \n System Uptime (ms): 10234567在Windows上使用Visual Studio Developer Command Prompt或已配置MSVC的终端:mkdir build cd build cmake .. -G Visual Studio 16 2019 -A x64 # 生成VS解决方案 cmake --build . --config Release # 编译 .\Release\sysinfo_tool.exe核心要点你需要为每个目标平台准备一套编译环境Linux上需要GCC/ClangWindows上需要MSVC并分别编译。最终得到的是两个完全不同的原生二进制文件。4.2 C#版本实现使用.NET 8项目结构使用命令行创建dotnet new console -n CSharpSysInfo cd CSharpSysInfoProgram.cs 文件内容using System.Runtime.InteropServices; // 定义平台互操作类使用条件编译 public static partial class NativeMethods { // Linux平台通过libc调用sysinfo [System.Runtime.Versioning.SupportedOSPlatform(linux)] [DllImport(libc, SetLastError true)] private static extern int sysinfo(ref SysInfo info); private struct SysInfo { public long uptime; // ... 其他字段省略 } public static long GetSystemUptimeLinux() { SysInfo info new SysInfo(); if (sysinfo(ref info) 0) { return info.uptime * 1000L; // 秒转毫秒 } return -1; } // Windows平台调用kernel32.dll的GetTickCount64 [System.Runtime.Versioning.SupportedOSPlatform(windows)] [DllImport(kernel32.dll)] private static extern ulong GetTickCount64(); public static long GetSystemUptimeWindows() { return (long)GetTickCount64(); } } class Program { static void Main(string[] args) { Console.WriteLine($Platform: {RuntimeInformation.OSDescription}); Console.WriteLine($Process ID: {Environment.ProcessId}); // .NET 5 新增属性 long uptime -1; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { uptime NativeMethods.GetSystemUptimeLinux(); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { uptime NativeMethods.GetSystemUptimeWindows(); } else { Console.WriteLine(Unsupported platform.); return; } Console.WriteLine($System Uptime (ms): {uptime}); } }项目文件CSharpSysInfo.csproj需要启用原生互操作支持仅Linux P/Invoke需要并标记目标平台Project SdkMicrosoft.NET.Sdk PropertyGroup OutputTypeExe/OutputType TargetFrameworknet8.0/TargetFramework !-- 允许不安全代码和原生互操作 -- AllowUnsafeBlockstrue/AllowUnsafeBlocks /PropertyGroup !-- 为Linux目标启用特定编译符号方便更细粒度控制 -- PropertyGroup Condition$(RuntimeIdentifier) linux-x64 DefineConstantsLINUX/DefineConstants /PropertyGroup /Project发布与运行发布为平台特定可执行文件推荐# 发布到当前系统开发机 dotnet publish -c Release # 发布为Linux-x64独立应用包含运行时 dotnet publish -c Release -r linux-x64 --self-contained true # 发布为Windows-x64独立应用 dotnet publish -c Release -r win-x64 --self-contained true执行publish命令后在bin/Release/net8.0/linux-x64/publish/或win-x64目录下会生成一个可执行文件如CSharpSysInfo你可以将这个目录整个拷贝到对应系统的机器上直接运行。发布为框架依赖应用dotnet publish -c Release --self-contained false这样生成的应用体积小但目标机器上必须安装对应版本的.NET运行时。核心要点你只需要一份C#源代码。通过dotnet publish命令指定不同的运行时标识符RID.NET SDK会自动处理底层的差异包括P/Invoke的库名映射生成针对该平台的可执行文件。对于简单的控制台应用你甚至可以直接将编译好的.dll文件拷贝到另一台装有.NET运行时的同架构机器上用dotnet CSharpSysInfo.dll运行。5. 深度解析性能、生态与适用场景的终极权衡选择C/C还是C#进行跨平台开发绝非简单的“哪个更好”而是对性能、开发效率、生态和控制力等多维度的权衡。5.1 性能与资源控制C/C胜在极致性能和精细控制。生成的代码是直接面向CPU和操作系统的原生机器码没有额外的运行时解释或JIT编译开销启动后。你可以手动管理内存虽然风险高、直接调用系统API、进行极致的算法优化。这对于游戏引擎Unreal Engine、高频交易系统、嵌入式设备、操作系统、数据库MySQL、PostgreSQL等对性能和资源有严苛要求的场景是唯一选择。C# (.NET)经过多年的优化.NET特别是.NET Core之后的性能已经非常出色在大多数业务应用场景中与C的差距可以忽略不计。但其垃圾回收GC机制决定了它无法提供确定性的实时性能GC会导致不可预测的短暂停顿。对于需要硬实时Hard Real-Time或极度敏感延迟的系统如航空电子、工业控制核心C#通常不适用。然而对于Web服务、企业应用、桌面工具、移动应用等.NET的性能是完全足够的甚至由于其强大的库和框架开发效率带来的优势远超微小的性能损失。5.2 开发效率与安全性C/C开发效率较低安全性挑战大。开发者需要手动管理内存malloc/free,new/delete极易引入内存泄漏、野指针、缓冲区溢出等严重安全问题。构建配置复杂依赖管理繁琐。现代CC11/14/17/20通过智能指针、RAII等特性大大改善了安全性但学习曲线陡峭。C#开发效率高内存安全。拥有自动垃圾回收、异常处理、丰富的标准库LINQ、异步编程模型等和强大的IDEVisual Studio, Rider支持。NuGet包管理器让依赖管理变得极其简单。这使得快速构建复杂、可靠的企业级应用成为可能。5.3 生态系统与第三方库C/C生态极其庞大且碎片化。有无数久经考验的库如Boost, OpenSSL, FFmpeg但库的质量、许可证、构建方式千差万别。集成它们可能需要处理复杂的编译脚本和依赖冲突。C# (.NET)生态统一且管理良好。NuGet是官方的包仓库绝大多数库都能通过一句dotnet add package命令轻松添加。.NET Standard/.NET 5的推行使得库的跨平台兼容性越来越好。但在某些特定领域如某些硬件驱动、极其底层的系统软件可能仍然缺乏成熟的.NET库需要借助P/Invoke调用C库。5.4 部署与分发C/C部署简单粗暴。将编译好的二进制文件以及必要的动态链接库.so/.dll打包扔到目标机器上就能运行。但需要确保二进制文件与目标系统的CPU架构、系统库版本如glibc兼容。这有时会导致“在我机器上好好的”问题。C#框架依赖部署应用体积小但要求目标机器安装对应版本的.NET运行时。对于服务器环境这通常不是问题可以统一安装。对于桌面端用户则增加了部署复杂度。独立部署.NET可以将运行时和应用一起打包生成一个完全独立、无需预装.NET的可执行文件。这解决了部署依赖问题但代价是应用体积会显著增大一个简单的“Hello World”可能从几MB膨胀到几十MB。.NET 8的Native AOT编译可以进一步将应用编译成真正的单一原生可执行文件体积和启动速度接近C但牺牲了部分动态特性如反射。5.5 决策指南我该如何选择为了更直观地帮助你决策可以参考以下对比表格特性维度C/C (编译移植)C# (.NET 跨平台)胜出方与说明性能极致无运行时开销可进行底层优化。优秀但有GC开销对大多数应用足够。C/C适用于性能敏感核心系统。内存控制完全手动控制风险与灵活性并存。自动垃圾回收安全但非实时。C/C用于需要确定性的场景C#用于追求开发安全性的场景。开发效率较低需处理内存、编译、依赖等复杂问题。极高现代语言特性、丰富库、强大IDE。C#显著缩短开发周期。跨平台成本高需为不同平台编写/维护代码和构建脚本。低大部分代码一份搞定工具链统一。C#.NET运行时是跨平台的基石。生态系统庞大但碎片化集成成本高。统一且管理良好(NuGet)集成简便。C#对于常规业务开发更友好。部署复杂度中等需处理二进制兼容性。灵活可选择依赖部署或独立打包。平手C#选项更多C更直接。适用场景操作系统、数据库、游戏引擎、嵌入式、驱动、高频交易、性能库。Web服务/API、企业应用、桌面GUI工具、移动应用、游戏脚本层、云计算服务。根据场景选择无绝对优劣。团队技能需要深厚的系统编程和调试功底。更侧重于业务逻辑和架构设计入门相对容易。根据团队现状选择。最终建议当你需要榨干最后一滴硬件性能、编写操作系统或驱动、在资源极度受限的嵌入式环境中工作或者维护一个已有的大型C/C代码库时选择C/C。当你需要快速构建和维护一个业务复杂的应用程序、团队希望更高的开发效率和更少的低级错误、项目主要涉及Web后端、企业软件、桌面工具或移动应用并且可以接受微小的性能妥协时选择C# (.NET)。在现代云原生和微服务架构下很多系统采用混合模式用C/C编写核心的高性能计算模块或专用库如图像处理、密码学然后通过P/Invoke或gRPC等机制被上层的C#/Go/Java业务服务调用。这种架构兼顾了性能与开发效率是值得借鉴的最佳实践。