从一个临时字节中返回Span 是危险的(使用时可能会产生异常)?

问题描述 投票:0回答:1

在.NET Core 3.0项目中,我有一个返回Span<byte>的接口。这适用于许多类,除了一个特定的实现,该实现可以动态生成其数据(由于不进行缓存)。

实现看起来像:

public Span<byte> Data => CompileBytes();

它将是这样的(这是抽象代码,但是非常接近用例)

public byte[] CompileBytes()
{
    using (MemoryStream stream = new MemoryStream())
    {
        foreach (IDataSource data in DataSources)
            stream.Write(data.ByteArray);
        return stream.ToArray();
    }
}

我一直在网上四处看看,是否可以保证这样做是安全的,但没有找到任何保证。

[我担心的是,Span是围绕数据的非常薄的一层,GC将忽略该数据,因此GC假定我们不会让span超过基础缓冲区,并且创建的临时字节数组最终将获得GC 'd,这意味着如果由于某些原因在其他人使用跨度的情况下进行了GC,则我有可能会在上面放置计时炸弹。是这样吗我可以为临时对象返回Span<>并完全可以吗(假设通过保持跨度的边界可以正确使用它)?

该定义似乎是依赖于实现的,所以我不能用有限的知识弄清楚它是否可以保留在引用中……因为如果这样,那么我很安全,并且我的问题得到了回答。

MSDN说“内存安全”,但是我不确定它们定义内存安全的确切细节以及是否涵盖了我的定义。这样,如果可以,则可以回答此问题。

我未使用任何unsafe代码。

c# .net-core
1个回答
1
投票

是,即使对Span<T>中的托管数组进行引用也是安全的。如文章All About Span: Exploring a New .NET Mainstay中所述,Span<T>使用一种特殊的方式来存储这些引用,即ByReference<T>,它是作为JIT内在函数实现的。]

引用链接的文章(如何实现跨度?

实际上,跨度在运行时中被编写为使用一种特殊的内部类型,该类型被视为即时(JIT)内在函数,而JIT为其生成了一个等效的ref T字段

和<

Span是一种类似于ref的类型,因为它包含一个ref字段,并且ref字段不仅可以引用数组之类的对象的开头,还可以引用其中的对象[...]这些引用称为内部指针,对.NET运行时的垃圾收集器而言,跟踪它们是相对昂贵的操作。

该引号的最后一部分澄清了Span<T>中存储的引用确实已由GC跟踪,因此它不会清除仍在引用的内存]