C# 需要解决方法来访问ReadOnlySpan<;T>;在返回IEnumerable的函数中

C# 需要解决方法来访问ReadOnlySpan<;T>;在返回IEnumerable的函数中,c#,.net-core,ienumerable,yield-return,C#,.net Core,Ienumerable,Yield Return,在处理本机代码互操作时,我决定是时候学习和尝试C语言的新Span特性了。尽管进行了多次试验,但一切都进展得非常顺利,直到我完成了非常长的功能的最后阶段,我在下面插入了一个最小的可复制样本: [DllImport(dll, SetLastError = true)] internal static extern void GetNativeData(out byte lpBuffer, int size, out bytesRead); ReadOnlySpan<T

在处理本机代码互操作时,我决定是时候学习和尝试C语言的新Span特性了。尽管进行了多次试验,但一切都进展得非常顺利,直到我完成了非常长的功能的最后阶段,我在下面插入了一个最小的可复制样本:

    [DllImport(dll, SetLastError = true)]
    internal static extern void GetNativeData(out byte lpBuffer, int size, out bytesRead);

    ReadOnlySpan<T> ReadArray<T>(ReadOnlySpan<byte> buf, int Length) where T : unmanaged
    {
        var size = Length * Unsafe.SizeOf<T>();
        if (buf.Length < size)
            buf = new byte[size];
            GetNativeData(out MemoryMarshal.GetReference(buf), size, out int read));
            Dh.CreateError(ReadMemoryErr);
        
        return MemoryMarshal.Cast<byte, T>(buf.Slice(0, size));
    }

    static IEnumerable<MyClass> GetResult()
    {
        // Here I allocate a buffer
        Span<byte> buf = new byte[1000];

        // After a long serie of calls to unmanaged DLL functions I end up with something like this:
        ReadOnlySpan<uint> uintRes = data.ReadArray<uint>(buf, 10);
        ReadOnlySpan<ushort> shortRes = data.ReadArray<ushort>(buf, 10);
        for (int i = 0; i < uintRes.Length; i++)
        {
           // Any access to spans inside this loop result in Error CS4013
           string r = GetFunRes(uintRes[i]);
           IntPtr r2 = GetFunRes2(shortRes[i]);
           yield return new MyClass() { Prop1 = r, Prop2 = r2 };
        }
    }

我将把这个问题标记为已结束,因为通过阅读本文,我自己找到了答案。基本上,为了使用IEnumerable和yield,需要使用
内存
,因为
Span
是在堆栈上分配的ref结构,不能跨yield边界使用。另一方面,
内存可以驻留在堆上,同时仍然提供
Span
访问。本文作者展示了pure-Span可以实现的最大功能,以及使用LINQ启用IEnumerable所需的示例代码,以及不同实现的基准测试<与其他解决方案相比,code>IEnumerable+memory
的速度非常慢,但对于仍然需要LINQ的人,我建议您看看,为了填补这一空白,由本文的同一作者开发的LINQ的替代版本。我不确定即使使用此解决方案,for/foreach循环与pure-Span组合是否仍会有很大的区别,但我认为这主要取决于调用的数量、查询的类型等(请参阅了解每个查询的执行情况)。 谢谢


另外,我注意到,有时托管文章的服务器无法访问,因此我在这里添加了一个,以确保将来始终可以访问该服务器。

您可能需要给出更具体的示例,因为在当前代码中,您既不需要
Span
也不需要
内存
显然您不能使用迭代器方法,将生成一个类,该范围将被捕获为属性,并最终位于堆上。。。。返回数组?或者列表,允许调用方使用循环,而不是调用迭代器方法。或者只使用toArray传递到迭代器。顺便说一句,如果内存已在堆上分配,您可能只返回
Span
本身,而不是对其进行迭代。谢谢您的帮助。我用一个更具体的例子和更多的细节更新了这篇文章。我从来没有机会玩过“内存”,我也从来没有觉得有必要使用它,因为所有的API大多都有跨度重载。然而,我注意到我可以访问在迭代器中分配了“内存”的缓冲区,但我仍然担心(我想如果我使用内存,那么我在Span上的所有工作都将被启动,并且仅仅是为了迭代器的需要).
但是我没能做到这一点
那么听起来你应该问一个关于什么“不起作用”的问题,而不是这个问题。我在链接的文章或您的代码中没有看到任何东西表明本地非异步方法方法不适合您。感谢您记录您找到的解决方案并提供有用的资源!如果您不需要其他答案,请随时将您自己的答案标记为已接受的答案,以便问题“关闭”:
int len = uintRes.Length;
for (int i = 0; i < len; i++)
{
   var res = ParseData(i);
   if (res == ExpectedResult())
      yield return res;
}
MyClass ParseData(int index)
{
     // CS8175: Cannot use ref local 'uintRes, shortRes' inside an anonymous method, lambda expression, or query expression
     string r = GetFunRes(uintRes[index]);
     IntPtr r2 = GetFunRes2(shortRes[index]);
     return new MyClass() { Prop1 = r, Prop2 = r2 };
}