当我试图传递一个看起来像的 ref 结构时,说
public ref struct BufferWriter
{
readonly Buffer* _pb;
public int WrittenBytes;
//...
public bool Append(ReadOnlySpan<byte> buffer)
{
//...
WrittenBytes += buffer.Length;
//...
}
}
对于方法(Write100U8Chars(BufferWriter, Object)
在下面的示例中),结构实际上不是通过引用传递的,而是通过值传递的:
protected override bool Serialize(object obj)
{
//...
fixed (Buffer* pb = &doc_body)
{
using (BufferWriter writer = new BufferWriter(pb))
{
writer.Append("UTF8 str"U8);
// pb->Bytes is now 8
// writer.WrittenBytes is now also 8
Write100U8Chars(writer, obj);
// pb->Bytes is now 108
// But writer.WrittenBytes is still 8!
}
}
//...
}
// A method that always calls BufferWriter.Append passing 100 UTF-8 chars (aka bytes)
protected abstract bool Write100U8Chars(BufferWriter writer, object obj);
由于using
块是一种只读上下文,正如我所想的那样,强制制作“防御性副本”,我试图删除块using
/指令:
fixed (Buffer* pb = &doc_body)
{
BufferWriter writer = new BufferWriter(pb)
writer.Append("UTF8 str"U8);
// writer.WrittenBytes is now 8
Write100U8Chars(writer, obj);
// No use of removing using: writer.WrittenBytes is still 8
}
做BufferWriter
一个ref readonly struct
没有区别。
将局部变量传递给带有参数的writer
方法仍然没有区别。Write100U8Chars(BufferWriter, Object)
in
如何ref struct
通过引用而不是像普通非引用结构那样通过值将 a 传递给抽象方法?
protected abstract bool Write100U8Chars(BufferWriter writer, object obj);
此调用writer
按值传递。如果你想通过引用传递它,它应该是:
protected abstract bool Write100U8Chars(ref BufferWriter writer, object obj);
据我了解,ref
结构声明中的 用于定义值类型的分配方式(仅堆栈),但它仍将表现为常规结构,默认情况下按值传递。
的目的ref struct
是允许结构使用只能允许存在于堆栈中的类型。典型示例是Span<T>
,但 C# 11 也允许引用字段。要点是,如果结构存储在堆上,则可能会违反内存安全。
据我所知,你的结构不需要这个,因为指针应该可以在常规结构中使用。这确实可能违反内存安全,但指针无论如何都需要不安全的代码,因此内存安全已经不在考虑范围之内。
如果你想传递对结构的引用,你需要在方法中声明它,即void MyMethod(ref BufferWriter)
ref 结构的行为与常规结构完全一样。它仍然是一个值类型,因此在方法调用中按值传递。它的唯一用途是产生编译错误,当结构可能最终在 GC 堆上或以其他方式需要引用并因此降低效率时发生。进行这些语法更改是为了使 C# 的行为更像 C++,但潜在的改进非常有限,而且肯定不会使 C# 更易于使用。仅当探查器告诉您可能值得付出努力时才使用它们。
@HansPassant 谢谢!好像我误解了ref真正的意思,而且我真的从来没有尝试过以这种方式使用 ref 结构。
无论如何,您使用固定指针有什么特别的原因吗?你可以Span单独使用 a
@Charlieface 通常你是对的。但是,这种方法Buffer通过提供调整大小的API进行封装,通常它的行为就像MemoryStream,但是在BufferWriter处理之后,它应该执行缓冲区的大小调整,因此我们需要保持指向对象的固定指针Buffer。无论如何,这只是图书馆的内部逻辑。顺便说一句,如果是 C# 11,aBuffer可能只是一个 ref 字段。但是代码是为 .NET 7.0 和 .NET Standard 2.1 编写的,因此指针将完成这两个框架的工作。
这是库用于生成一批可能非常大的文件的方法。缓冲区由 malloc 创建,与 calloc 不同,它不会将内存清零,分配托管缓冲区或使用任何包装器,例如MemoryStream. 分配的内存中充满了随机剩余数据,我们可以安全地写入其中,但其余未使用的垃圾应该被截断。
这个答案需要解决如何public ref struct BufferWriter不影响参数发生的事情ref。
我明白了writer.WrittenBytes,但在哪里writer.bytes?