【问题标题】:Passing both stackalloc'ed Span<T> and by-ref struct as arguments将 stackalloc 的 Span<T> 和 by-ref 结构作为参数传递
【发布时间】:2020-08-06 06:08:25
【问题描述】:

我正在使用SequenceReader&lt;T&gt; 将内容从ReadOnlySequence&lt;T&gt; 复制到不同的Span&lt;T&gt;。但是,当我尝试将复制逻辑封装到单独的函数中并希望使用堆栈上分配的 Span&lt;T&gt; 来调用它时,出现编译器错误。

var sequence = new ReadOnlySequence<byte>(new byte[20]); // Source data to copy

Span<byte> heap = new byte[10]; // Target location 1
Span<byte> stack = stackalloc byte[10]; // Target location 2

var reader = new SequenceReader<byte>(sequence);

TryReadInto(ref reader, heap); // This is fine
TryReadInto(ref reader, stack); // Gives compile time error CS8350

由于SequenceReader&lt;T&gt; 上的实例方法TryCopyTo 没有让读者受益,我创建了一个更高级别的函数来处理这个问题:

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool TryReadInto(ref SequenceReader<byte> reader, Span<byte> destination)
{
    if (reader.TryCopyTo(destination)) {
        reader.Advance(destination.Length);
        return true;
    }

    return false;
}

只要目标是堆分配的,一切正常。但是,当使用stackalloc 分配目标时,编译器会报错CS8350:不允许使用这种参数组合,因为它可能会将参数引用的变量暴露在其声明范围之外

当我不将阅读器作为参考传递时,编译器错误就会消失,但自SequenceReader&lt;T&gt; is a mutable struct 以来需要将阅读器作为参考传递,并且该位置在函数内是高级的。

我得到编译器错误。参数reader 可以在较早的堆栈帧中分配,并且作为类似ref 的结构将能够将Span&lt;T&gt; 作为字段。方法体可以使第二个(堆栈分配的)参数 destination 转义它自己的堆栈帧,因此在 C# 中同时使用 ref 参数和堆栈分配的 Span&lt;T&gt; 的函数调用是非法的。

但是,我该如何封装我的逻辑呢? SequenceReader&lt;T&gt; 是一个封闭类型,所以我不能只添加一个实例方法。当我手动内联函数调用时,我没有收到编译器错误。我猜编译器看到读取器被分配在与堆栈分配的Span&lt;byte&gt; 相同的堆栈帧中。因为SequenceReader&lt;T&gt;is a mutable struct,所以不能通过值传递读者。

【问题讨论】:

    标签: c# .net-core clr


    【解决方案1】:

    您已经看到编译器试图阻止的问题。

    如果我将您所描述的内容翻译成 C#,代码可能如下所示:

    // This is a struct lives on stack
    ref struct SomeStructOnStack
    {
        public Span<byte> Something;
    }
    
    // This method saves "stack" to the "obj"
    static void SomeMethod(ref SomeStructOnStack obj, Span<byte> stack)
    {
        obj = new SomeStructOnStack
        {
            Something = stack
        };
    }
    
    // This method accepts byref "obj" from its caller
    // In such case, "stack" will escape its scope
    // This is what CS8350 is for
    void Test(ref SomeStructOnStack obj)
    {
        Span<byte> stack = stackalloc byte[20];
        SomeMethod(ref obj, stack);
    }
    

    ref structstack 在同一个堆栈帧中时,我同意你的看法,我们不应该看到编译器错误。但目前不支持。

    要解决此问题,您可以像这样创建Span&lt;byte&gt; 的副本:

    Span<byte> stack = stackalloc byte[20];
    
    unsafe
    {
        fixed (byte* pStack = stack)
        {
            var copy = new Span<byte>(pStack, stack.Length);
            TryReadInto(ref reader, copy);
        }
    }
    

    【讨论】:

    • 很有趣,所以通过回退到不安全的指针逻辑,我们可以告诉编译器我们知道我们在做什么,它不应该抱怨,只需编译该代码。我感觉这是安全代码的限制,所以我已经提交了an issue
    猜你喜欢
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 2018-01-20
    • 2021-04-30
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多