从临时字节[]返回Span <byte>是否很危险(使用时可能会产生异常)?

时间:2019-10-09 19:30:04

标签: c# .net-core

在.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,这意味着如果由于某些原因在其他代码正在使用span的情况下进行了GC,则我可能会碰到定时炸弹。是这样吗我是否可以为临时对象返回Span<>并可以正常使用(假设在跨度范围内可以正确使用它)?

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

MSDN说“内存安全”,但是我不确定它们定义内存安全的确切细节以及是否涵盖了我的定义。这样,如果可以,那么这个问题就会得到解决。

我没有使用任何unsafe代码。

1 个答案:

答案 0 :(得分:2)

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

引用链接的文章(如何实现Span<T>):

  

Span<T>实际上是为了在运行时中使用特殊的内部类型而编写的,该内部类型被视为即时(JIT)内在函数,并且JIT为其生成了等效于ref T字段

以及什么是Memory<T>和为什么需要它?

  

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

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