【问题标题】:What is a working alternative to being unable to pass a Span<T> into lambda expression?除了无法将 Span<T> 传递到 lambda 表达式之外,有什么可行的替代方法?
【发布时间】:2020-01-06 02:04:41
【问题描述】:

我有这种形状的功能,它可以进行一维寻根:

public delegate double Fun(double x, object o);

public static void Solve(Fun f, out double y, object o) 
{
    y = f(1.0, o);  // all the irrelevant details of the algorithm omitted
}

这是一个固定的形状,以使算法可重用。考虑这是一个我无法更改的固定库函数(或者至少需要保持通用和可重用,并且不会针对这个问题的具体情况进行更改)。

我想传入一个函数,该函数需要在堆栈上保存Span&lt;T&gt;s 的外部参数以避免分配,但显然不能将Span&lt;T&gt;s 推入对象,因为这需要装箱和拆箱.

使用 lambda 表达式,调用代码可能类似于:

void CallingMethod()
{
   Span<double> k1  = stackalloc double[n];
   double answer;
   Solve((x, o) => Wrapper(x, k1, o), out answer, null);
}

double Wrapper(double x, ReadOnlySpan<double> k1, object o)
{
   return <some function of x and k1>;
}

但这不起作用,因为您无法使用 lambda 表达式在 Span&lt;T&gt;s 上形成闭包。它们也不能在泛型类型中使用,任何装箱和拆箱都不可用,不能作为 params 关键字传递,不能用于实例变量等。

有没有办法解决这个问题?

只是为了强调这个例子过于简化了。我可能有一个跨度,但我目前正在处理的问题需要通过 4 个跨度。我需要为任意数量的它们进行设计。

【问题讨论】:

  • 记住 Span&lt;T&gt; 代表堆栈上的内存 - 所以它不是关于装箱,而是更多地与 CLR 不能(还)保证 lambda 的闭包不会超过调用堆栈帧。
  • 你不能用Memory&lt;T&gt;吗?
  • 我使用Span&lt;T&gt; 来专门避免堆分配,Memory&lt;T&gt; 在堆上
  • 你能用Memory&lt;T&gt; + ArrayPool&lt;T&gt; 来避免新的分配吗?
  • 我认为您无法避免 lambda 发生内存分配。定义自己的 ValueTypeFun 实现,来自己控制生命周期怎么样?

标签: c#


【解决方案1】:

跨度不能用于捕获的变量。然而,指针可以。这可能会消除跨度的最佳特性,但在一般情况下,您可以修复跨度,并捕获固定指针,但您需要真的非常小心以确保委托调用不能逃脱fixed 块,即您没有在任何地方分发委托。获得固定指针后,您可以在内部重新创建跨度:

Span<double> k1 = // ...
// ...
fixed (double* ptr = k1)
{
    var evil = ptr; // this is me convincing the compiler that
                    // I've considered whether the delegate could
                    // outlive the delegate invocation
    Solve((x, o) => Wrapper(x, new Span<double>(evil, k1.Length), o), out answer, null);
}

但是,在您的 特定 情况下,如果您只是要固定它(当它已经有效地固定在堆栈上时),您可能根本不关心原始跨度. “不要让委托超过堆栈框架”的要求仍然存在,但编译器对此的要求稍低一些,因为您已经违反了所有规则:

double* k1 = stackalloc double[n];
// ...
Solve((x, o) => Wrapper(x, new Span<double>(k1, n), o), out answer, null);

显然,这两个都需要unsafe 修饰符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-31
    • 2015-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-04
    相关资源
    最近更新 更多