英文:
Why passing ref struct to methods actually passes a copy of a ref struct?
问题
当我试图传递一个看起来像这样的 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
被传递到抽象方法中时保持引用而不是像普通非 ref 结构体那样被传递值?
英文:
When I'm trying to pass a ref struct that looks like, say
public ref struct BufferWriter
{
readonly Buffer* _pb;
public int WrittenBytes;
//...
public bool Append(ReadOnlySpan<byte> buffer)
{
//...
WrittenBytes += buffer.Length;
//...
}
}
to a method (Write100U8Chars(BufferWriter, Object)
in the following example), a struct is not actually passed by a reference, but by the value:
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);
Because of the using
block being kind of read-only context which, as I supposed, enforces making "defensive copies", I've attempted to remove the using
block/directive:
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
}
Making BufferWriter
a ref readonly struct
makes no difference.
Passing a writer
local to a method Write100U8Chars(BufferWriter, Object)
with in
parameter still makes no difference.
How can I make a ref struct
be passed into abstract method by reference and not by value like it is the case with ordinary non-ref structs?
答案1
得分: 4
保护的抽象方法,写入100个U8字符,传递 writer
的方式是按值传递。如果要按引用传递,应该是:
protected abstract bool Write100U8Chars(ref BufferWriter writer, object obj);
据我理解,在结构声明中使用 ref
是为了定义值类型如何分配(仅堆栈),但它仍将像常规结构一样按值传递。
英文:
protected abstract bool Write100U8Chars(BufferWriter writer, object obj);
This call is passing writer
by value. If you want to pass it by reference, it should be:
protected abstract bool Write100U8Chars(ref BufferWriter writer, object obj);
As far as I understand it, the ref
in the struct declaration is used to define how the value type can be allocated (only stack) but it will still behave as a regular struct and get passed by value by default.
答案2
得分: 2
ref struct
的目的是允许结构体使用只能存在于堆栈上的类型。典型示例是 Span<T>
,但C# 11也允许引用字段。主要观点是如果将结构体存储在堆上,可能会违反内存安全性。
据我所知,您的结构体似乎不需要这个,因为指针应该可以在常规结构体中使用。这确实可能会违反内存安全性,但指针需要使用不安全代码,因此内存安全性已经不存在。
如果您想传递对结构体的引用,需要在方法中声明,即void MyMethod(ref BufferWriter)
。
英文:
The purpose of ref struct
is to allow the struct to use types that can only ever be permitted to exist on the stack. The typical example is Span<T>
, but C# 11 also allow ref fields. The main point is that memory safety can be be violated if the struct is stored on the heap.
As far as I can tell, your struct does not need this, since a pointer should be possible to use in a regular struct. This does potentially violate memory safety, but pointers need unsafe code anyway, so memory safety is out the window already.
If you want to pass a reference to a struct you need to declare that in the method, i.e. void MyMethod(ref BufferWriter)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论