首页 > 解决方案 > 什么是错误“类型实例化涉及 byref 类型。” 什么是 F# 中的解决方法

问题描述

我有一些包装 TA-Lib 的代码,并且很多包装器非常相似:

let sma (timePeriod: int) (data: float[]) =
    let mutable outStartIndex = 0
    let mutable outNbElement = 0

    let mutable smaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = Core.Sma(0, (data.Length - 1), data, timePeriod, &outStartIndex, &outNbElement, smaData)

    if retCode <> Core.RetCode.Success then
        invalidOp (sprintf "AssertRetCodeSuccess")

    let padding = Array.create (timePeriod - 1) System.Double.NaN
    Array.append padding smaData



let ema (timePeriod: int) (data: float[]) =
    let mutable outStartIndex = 0
    let mutable outNbElement = 0
    let mutable emaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = Core.Ema(0, (data.Length - 1), data, timePeriod, &outStartIndex, &outNbElement, emaData)

    if retCode <> Core.RetCode.Success then
        invalidOp (sprintf "AssertRetCodeSuccess")

    let padding = Array.create (timePeriod - 1) System.Double.NaN
    Array.append padding emaData

我想做的是创建一个通用函数,我可以在其中传递 TA-Lib 函数来调用。就像是:

let myGenericFunction (timePeriod: int) (data: float[]) TALibFunc =
    let mutable outStartIndex = 0
    let mutable outNbElement = 0

    let mutable smaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = TALibFunc(0, (data.Length - 1), data, timePeriod, &outStartIndex, &outNbElement, smaData)

    if retCode <> Core.RetCode.Success then
        invalidOp (sprintf "AssertRetCodeSuccess")

    let padding = Array.create (timePeriod - 1) System.Double.NaN
    Array.append padding smaData

但我得到的错误是:

[FS0412] 类型实例化涉及 byref 类型。Common IL 的规则不允许这样做。

有解决方法吗?我不熟悉这个问题。

标签: f#

解决方案


简短回答:将您的mutable参数替换为ref.


TA-Lib 有一个非常不幸的 API:那些讨厌的out参数(在 F# 中称为byref),它们总是很麻烦。在这种情况下,它们不能是泛型类型实例化的一部分。

这是一个更短的例子。考虑好旧的list<T>。我们可以做一个空的list<int>

let noInts = [] : list<int>

但是如果那些ints 是 sbyref呢?

let noRefs = [] : list< byref<int> >

不能做 - 编译器说。类型实例化涉及 byref 类型。Common IL 的规则不允许这样做。对不起。


在您的情况下,of 的最后一个参数myGenericFunction是 F# 函数。在 F# 中,函数由类型表示FSharpFunc<T, R>(其中T是参数和R结果)。所以你最后一个参数的类型是这样的:

FSharpFunc< int * int * float array * int * byref<int> * byref<int> * float array, int >

看到里面的那两个byref<int>s了吗?那些是&outStartIndex&outNbElement。并且它们在通用实例化中是被禁止的。倒霉。


但有希望!

mutable关键字只是在 F# 中制作可变单元格的两种方法之一。另一种方法是ref

let x = ref 0     // Initialization
printfn "%d" !x   // Prints "0"
x := 42           // Mutation
printfn "%d" !x   // Prints "42"

这是一个老派的东西,早于mutable,被实现为一个库(而不是语言结构),并且在大多数情况下mutable更好。但这不是其中之一!

事实证明:

  1. 与真正的 .NET CILout参数不同,ref单元格可以作为通用实例化的一部分。因为,从 .NET 的角度来看,它们并没有什么特别之处——只是另一个类。
  2. F# 编译器对它们有特殊的作用:当预期的类型是 a ref,但你试图传递一个带有out-parameter 的函数时,编译器会自动为你生成一些包装代码。

因此,有了这些知识,您可以myGenericFunction像这样进行修改:

let myGenericFunction (timePeriod: int) (data: float[]) TALibFunc =
    let outStartIndex = ref 0
    let outNbElement = ref 0

    let mutable smaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = TALibFunc(0, (data.Length - 1), data, timePeriod, outStartIndex, outNbElement, smaData)

    ...

然后消费者可以这样称呼它:

myGenericFunction 42 [|1; 2; 3|] Core.Sma // Wrapping code gets generated here

推荐阅读