我使用 IL 反汇编程序查看了 ReadOnlyMemory 结构体 (.NET 461) 的 Length 属性的代码,发现了这个奇怪的代码:
public int Length => this._length & int.MaxValue;
这里与 int.MaxValue 进行 AND 运算的原因是什么?
(此答案仅与 System.Memory.dll
的当前最新版本专门用于 .NET Framework 4.x ,而不是 .NET Core 和 .NET 5+ 附带的版本)
(作为style的免责声明:我看到Memory<T>
的作者选择使用下划线前缀作为状态字段名称 - 这不是我自己会做的事情 - 我只是逐字使用他们的字段名称为了清楚起见)顺便说一句,
Memory<T>
和
ReadOnlyMemory<T>
具有相同的布局 - 唯一的区别在于它们暴露的界面设计,所以我在这里互换使用术语
ReadOnlyMemory<T>
和
Memory<T>
。
Memory<T>
类型是一种需要轻量级的
struct
类型,这意味着它不能拥有超过绝对必要的字段:
Memory<T>
中,这由名为
Object
的不透明
_object
字段表示,因为它不仅可以表示
Byte[]
,还可以表示
Char[]
或
String
或
Span<T>
的任何其他类型 可以合作。
Int32 _length
Int64
Int32 index
into
Memory<T>
packed)已经消耗了 x86 上的
32 + 32 + 32 = 96 bytes
,或者 x64 上的 64 + 32 + 32 = 128 bytes
struct` 可以变成。...但肯定 - which is pushing at the limit for how big a
还需要存储有关 Memory<T>
type的信息,否则在执行 之前需要依赖(非常昂贵的)运行时类型测试(例如
_object
运算符)任何行为都会对性能造成破坏。
具体来说,
is
需要知道Memory<T>
的引用——或者是否不是。这是一个简单的 2 状态值,可以用内存中的单个位表示,而
_object
(C# 中的
System.Boolean
)类型使用整个 8 位(以及编组到 Win32 时的 四个字节) bool
值)。因此 BOOL
需要一个 1 位字段,并且它对 Memory<T>
和 _index
偷偷地占用了
_length
的 MSB 用于存储“是否固定内存”状态。
在 .NET Core 和 .NET 5+ 的 _length
的最新版本中,他们交换了字段,现在使用 System.Memory
字段来存储标志,而不是 _index
_length
32b491939fbd125f304031c35038b1e14b4e3958
)。
System.Memory.dll
...但对于 .NET Core / .NET 5+:
引用来源:
因此,为了让
_object
属性返回有意义的值,它不能简单地按原样返回Length
字段;它需要首先消除标志位,否则其消费者可能会认为他们手上有 4GB 分配。