互操作(Interop)让 .NET 代码可以调用原生 C/C++ 库和 COM 组件,这在图像处理、硬件驱动、游戏引擎集成等场景中不可或缺。源代码生成器(Source Generator)则是 Roslyn 编译器的扩展能力,可以在编译时分析代码并生成额外的源文件。本文将深入探讨这两项技术。
P/Invoke 基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System.Runtime.InteropServices;internal static partial class NativeMethods { [LibraryImport("user32.dll" , SetLastError = true) ] internal static partial int MessageBoxW ( IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr )] string text, [MarshalAs (UnmanagedType.LPWStr )] string caption, uint type) ;} NativeMethods.MessageBoxW(IntPtr.Zero, "Hello" , "Native" , 0 );
DllImport vs LibraryImport
特性
DllImport
LibraryImport(.NET 5+)
生成方式
运行时反射
编译时源码生成
性能
有 P/Invoke 开销
优化后开销更小
安全性
自动处理
需要 partial 方法
可用性
所有 .NET 版本
.NET 5+
数据封送(Marshalling) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode) ] public struct Point{ public int X; public int Y; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32) ] public string Name; } internal static partial class NativeCalls { [LibraryImport("mylib.dll" ) ] internal static partial int ProcessPoint (ref Point point ) ; [LibraryImport("mylib.dll" ) ] internal static partial void ProcessArray ( [MarshalAs(UnmanagedType.LPArray )] byte [] data, int length) ; [LibraryImport("mylib.dll" ) ] internal static partial void SetCallback ( delegate * unmanaged [Cdecl]<int , void > callback ) ; [LibraryImport("mylib.dll" ) ] [return: MarshalAs(UnmanagedType.LPWStr) ] internal static partial string GetString () ; } unsafe { delegate * unmanaged [Cdecl]<int , int , int > add = &NativeAdd; int result = add (3 , 4 ); } static int NativeAdd (int a, int b ) => a + b;
Span 在互操作中的应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 internal static partial class NativeMemory { [LibraryImport("kernel32.dll" , SetLastError = true) ] internal static partial void CopyMemory ( IntPtr destination, IntPtr source, nuint length ) ;} unsafe { var managed = new byte [] { 1 , 2 , 3 , 4 , 5 }; var native = Marshal.AllocHGlobal(5 ); try { fixed (byte * pManaged = managed) { NativeMemory.CopyMemory(native, (IntPtr)pManaged, (nuint )managed.Length); } Span<byte > span = new Span<byte >(native.ToPointer(), 5 ); Console.WriteLine(span[0 ]); } finally { Marshal.FreeHGlobal(native); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Type excelType = Type.GetTypeFromProgID("Excel.Application" )!; dynamic excel = Activator.CreateInstance(excelType)!;excel.Visible = true ; excel.Workbooks.Add(); excel.Cells[1 , 1 ] = "Hello" ; excel.Cells[1 , 2 ] = "World" ; Marshal.ReleaseComObject(excel);
源代码生成器(Source Generator) 这是 C# 9 引入的编译时元编程能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 [Generator ] public class AutoNotifyGenerator : IIncrementalGenerator { public void Initialize (IncrementalGeneratorInitializationContext context ) { var declarations = context.SyntaxProvider .CreateSyntaxProvider( predicate: (node, _) => node is ClassDeclarationSyntax, transform: (ctx, _) => ctx.Node as ClassDeclarationSyntax) .Where(c => c is not null ); context.RegisterSourceOutput(declarations, GenerateCode); } private void GenerateCode ( SourceProductionContext context, ClassDeclarationSyntax classDecl ) { var className = classDecl.Identifier.Text; var namespaceName = "Generated" ; var code = $$"" " namespace {{namespaceName}} { public partial class {{className}} { public string GeneratedProperty => " 由 Source Generator 生成"; } } " "" ; context.AddSource($"{className} .g.cs" , code); } } [AttributeUsage(AttributeTargets.Field) ] public class AutoNotifyAttribute : Attribute { }[AutoNotify ] private string _name;
常用 Source Generator 库
库
功能
System.Text.Json
编译时 JSON 序列化
MediatR
请求处理管道
Mapperly
编译时对象映射
GeneratedRegex (.NET 7+)
编译时正则表达
StronglyTypedId
强类型 ID
1 2 3 4 5 [GeneratedRegex(@"\d{3}-\d{4}" ) ] private static partial Regex PhoneRegex () ;bool isValid = PhoneRegex().IsMatch("123-4567" );
实战:编译时 JSON 序列化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [JsonSourceGenerationOptions(WriteIndented = true) ] [JsonSerializable(typeof(Person)) ] [JsonSerializable(typeof(Order[ ]))] internal partial class AppJsonContext : JsonSerializerContext { } var options = new JsonSerializerOptions{ TypeInfoResolver = AppJsonContext.Default }; var person = new Person { Name = "张三" , Age = 28 };string json = JsonSerializer.Serialize(person, AppJsonContext.Default.Person);var deserialized = JsonSerializer.Deserialize(json, AppJsonContext.Default.Person);
性能对比
方案
序列化/调用性能
启动速度
二进制体积
反射序列化
较慢
快
小
Source Generator 序列化
接近手写
快
略大(多生成代码)
动态代码生成(Reflection.Emit)
非常快
较慢(JIT)
小
P/Invoke(DllImport)
较快
中等
依赖原生库
P/Invoke(LibraryImport)
更快
快
依赖原生库
安全注意事项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 IntPtr ptr = GetNativeBuffer(); if (ptr == IntPtr.Zero) throw new InvalidOperationException("指针为空" ); public class MySafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public MySafeHandle () : base (true ) { } protected override bool ReleaseHandle () { return NativeMethods.FreeResource(handle) == 0 ; } } if (!NativeMethods.SomeApi()){ int error = Marshal.GetLastPInvokeError(); }
提升互操作性能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [LibraryImport("mylib.dll" , SetLastError = false) ] public struct BlittablePoint { public int X; public int Y; } fixed (int * p = array){ NativeProcess(p, array.Length); } [LibraryImport("mylib.dll" ) ] internal static partial void RegisterCallback ( delegate * unmanaged [Cdecl]<int , void > callback ) ;
总结 :互操作让 .NET 能够充分利用已有的原生生态,源代码生成器则将运行时代价转移至编译时。在实际项目中,应优先选择 LibraryImport 而非 DllImport、SafeHandle 而非 IntPtr、Source Generator 而非反射。理解数据封送规则、blittable 类型和函数指针的用法,可使互操作代码既安全又高效。