我正在尝试将F#样式的接口添加到具有byref返回方法的类型中。 代码如下:
type IPool<'P, 'T when 'T: struct> =
abstract member GetReference: ITypedPointer<'P, 'T> -> byref<'T>
let Ref<'TPool, 'P, 'T when 'TPool :> IPool<'P, 'T>> (pool: 'TPool) pointer =
pool.GetReference pointer
现在令我惊讶的是,在我引入IPool
界面之前,类似的事情工作正常。在此之前,Ref 本身包含一个像&pool.data.[idx]
这样的实现,并且工作正常。
我尝试安装F#工具的夜间构建,因为最新版本不正式支持byref返回,并且最近完成了引入它们的PR:https://github.com/Microsoft/visualfsharp/pull/4888
但是,我仍然在Visual Studio中得到error FS3209: The address of the variable 'copyOfStruct' cannot be used at this point. A method or function may not return the address of this local value.
。outref<T>
型似乎也仍然不可用。我错过了什么吗?
我还尝试删除pointer
参数,只需返回pool.GetReference
只会收到不同的错误消息。
补充:最终目标是能够做到
let aref = Ref pool ptr
let bref = Ref pool ptr
aref <- 42
assert(aref = bref)
例如,给调用者一个对内部存储器的直接引用,通常由一个数组支持,类似于Span<T>
。我这样做是出于性能原因,因此不可以每次调用 Ref 进行分配。
出于某种原因,减少泛化有助于消除错误:
let Ref<'P, 'T when 'T: struct> (pool: IPool<'P, 'T>) pointer = pool.GetReference pointer
解决方案由
https://github.com/Microsoft/visualfsharp/issues/5366#issuecomment-407521220
虽然它没有解释为什么原始代码无法编译。
我认为返回byref
类型不是标准做法。 此类型实际上适用于方法参数,主要用于具有out
或ref
参数的 C# 互操作。 看看这个 StackOverflow 问题以获得很好的解释。
您可以做的是更改接口上的方法,以采用ITypedPointer<'P,'T>
元组并byref<'T>
(不允许使用byref
与柯里参数一起使用(并返回unit
。 然后,可以像使用 C# 中的out
参数调用任何标准 .NET 方法一样调用GetReference
。 那看起来像这样:
type ITypedPointer<'P, 'T> = interface end
type IPool<'P, 'T when 'T: struct> =
abstract member GetReference: ITypedPointer<'P, 'T> * byref<'T> -> unit
let Ref<'TPool, 'P, 'T when 'TPool :> IPool<'P, 'T>> (pool: 'TPool) pointer =
let mutable value = Unchecked.defaultof<'T>
pool.GetReference(pointer, &value)
value