当我试图传递一个看起来像的 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?