.NET9 Pre4 UnsafeAccessor泛型用法
.NET9 PreView4 CLR里面添加了对于UnsafeAccessorAttribute特性泛型的支持。而对于UnsafeAccessorAttribute本身的支持则在.NET8里面。本篇看下Pre4里面的这个特性用法以及原理。
来看一个简单的使用UnsafeAccessorAttribute的例子:
internal class Program
{
public class Class<T>
{
private T _field;
private void M<U>(T t, U u) { Console.WriteLine(t);Console.WriteLine(u); }
}
class Accessors<V>
{
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field")]
public extern static ref V GetSetPrivateField(Class<V> c);
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "M")]
public extern static void CallM<W>(Class<V> c, V v, W w);
}
public static void AccessGenericType(Class<int> c)
{
ref int f = ref Accessors<int>.GetSetPrivateField(c);
f = 10;
Console.WriteLine(f);
Accessors<int>.CallM<string>(c, 1, "hello");
}
static void Main(string[] args)
{
Class<int> c1 = new Class<int>() { };
AccessGenericType(c1);
Console.ReadLine();
}
}
上述代码取自官方示例,稍微改了下。Class<T>里面的一个字段T及函数M,通过UnsafeAccessorAttribute进行访问和赋值。这里需要注意的点是,如果通过UnsafeAccessorAttribute访问字段,则UnsafeAccessorAttribute特性声明的方法参数需是类。比如本例的GetSetPrivateField它的参数需要字段所在类。如果是通过UnsafeAccessorAttribute特性访问方法,比如本例的CallM访问M方法,则它的方法(Call)第一个参数是方法(M)所在类(Class<V>),后面是M方法的参数(T t, U u),顺序相同。
(注意以上代码需在.NET9 PreView4里面运行,vs开启preview版本方法:工具>选项>环境>预览功能>使用.NET SDK预览版勾选,下载.NET9 Preivew4安装,重启VS即可),结果打印如下:
10
1
hello
原理其实也比较简单,以GetSetPrivateField为例(也可以看看CLR的GenerateAccessor)。这个函数里面被roslyn compile了一个.cctor
.method public hidebysig static !V& GetSetPrivateField(class ConsoleApp1.Program/Class`1<!V> c) cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.UnsafeAccessorAttribute::.ctor(valuetype [System.Runtime]System.Runtime.CompilerServices.UnsafeAccessorKind) = ( 01 00 03 00 00 00 01 00 54 0E 04 4E 61 6D 65 06 // ........T..Name. 5F 66 69 65 6C 64 ) // _field
}
JIT导入加载之后,会被识别出它是byref(即C#里的ref),它的加载实际上是.ctor的引用结果放到栈(ldflda)上
IL to import:
IL_0000 02 ldarg.0
IL_0001 7c 01 00 00 0a ldflda 0xA000001
IL_0006 2a
然后识别,看到此时JIT已经知道它是byref了
STMT00000 ( 0x000[E-] ... ??? )
[000002] ---X------- * RETURN byref
[000001] ---X------- \--* FIELD_ADDR byref ConsoleApp1.Program+Class`1[int]:_field
如此即可通过UnsafeAccessorAttribute特性访问_field字段了。那么JIT实际上是把GetSetPrivateField变形成了如下:
伪代码:
ref GetSetPrivateField()
{
return c1._field;
}
更新于:6个月前相关文章
- .NET C#中的Func、Predicate和Expression用法详解
- 5个高性能 .NET Core 图片处理库推荐
- ASP.NET如何将Views文件夹从项目分离
- .NET C# 读取编辑.AVIF图片文件
- .NET C# SkiaSharp读取.AVIF图片文件报错
- .NET开源ORM FreeSql常见问题和解决方法
- 微软于发布了.NET 9 Release Candidate 2 提高整体质量
- 分享5个开源的.NET Excel读写操作库
- ASP.NET 使用Entity Framework (EF) 创建迁移修改SQLite数据库表结构
- 如何从.NET Framework迁移到.NET Core或.NET 6/7?
- 如何优化ASP.NET Core应用的性能?
- 10款.NET开发中推荐的代码分析和质量工具
- .NET9 Blazor有哪些更新?
- 在Docker、Kubernetes环境下部署.NET应用的最佳实践
- .NET 游戏开发框架有哪些?
- PluginCore 基于 ASP.NET Core 的轻量级插件框架
- .NET C# EntityFramework(EF)连接SQLite代码示例
- Sylvan.Data.Excel 性能优异的开源.NET Excel数据读取库
- ASP.NET Core 中常用的内置中间件
- .NET9 F#有什么新特性?