F#相当于C#' out'

时间:2015-02-24 08:32:50

标签: f# c#-to-f#

我正在将C#库重写为F#,我需要翻译以下代码

bool success;
instance.GetValue(0x10, out success);

F#中的out关键字的等价物是什么?

4 个答案:

答案 0 :(得分:14)

无论是wasatz的答案还是Max Malook都没有完成。使用out参数调用方法有三种方法。第二种和第三种方式也适用于ref参数。

对于示例,假设以下类型:

open System.Runtime.InteropServices //for OutAttribute
type SomeType() =
    member this.GetValue (key, [<Out>] success : bool byref) =
        if key = 10 then
            success <- true
            "Ten"
        else
            success <- false
            null

还假设我们有一个该类型的实例:

let o = SomeType()

选项1

您可以让F#编译器通过使用返回值对其进行元组处理out参数:

let result1, success1 = o.GetValue 10
let result2, success2 = o.GetValue 11

在F#interactive中运行上述行会产生

val success1 : bool = true
val result1 : string = "Ten"
val success2 : bool = false
val result2 : string = null

选项2

您可以使用可变值,并使用&运算符传递其地址:

let mutable success3 = false
let result3 = o.GetValue (10, &success3)
let mutable success4 = false
let result4 = o.GetValue (11, &success4)

在F#interactive中,结果是

val mutable success3 : bool = true
val result3 : string = "Ten"
val mutable success4 : bool = false
val result4 : string = null

当您委托给另一个方法时,此选项最佳,因为您可以将调用方法的out参数直接传递给被调用的方法。例如,如果要围绕IDictionary<_,_>实现包装,则可以将TryGetValue方法编码为

//...
interface IDictionary<'TKey, 'TValue> with
    member this.TryGetValue (key, value) = inner.TryGetValue (key, &value)
    //...

选项3

您可以使用参考单元格:

let success5 = ref false
let result5 = o.GetValue (10, success5)
let success6 = ref false
let result6 = o.GetValue (11, success6)

输出:

val success5 : bool ref = {contents = true;}
val result5 : string = "Ten"
val success6 : bool ref = {contents = false;}
val result6 : string = null

警告!

注意不要像在C#中那样使用ref关键字作为输入/输出参数。例如,以下内容未产生预期结果:

let success7 = false
let result7 = o.GetValue (10, ref success7)

输出:

val success7 : bool = false
val result7 : string = "Ten"

为什么success7保留值false?因为success7是一个不可变的变量。

在C#中,ref提醒您注意这样一个事实,即您将对变量的引用作为ref参数的参数传递。它只是作为保险,调用者的程序员知道变量可以被被调用的方法修改。但是,在F#中,ref创建一个新的引用单元格,其中包含以下表达式值的副本

在这种情况下,我们创建一个引用单元格,其中包含从success7变量复制的值,但不会将该新引用单元格分配给任何变量。然后,我们将该引用单元传递给GetValue方法,该方法修改引用单元格的内容。因为调用方法没有指向修改过的单元格的变量,所以它无法读取引用单元格的新值。

答案 1 :(得分:6)

你可能应该返回一个选项或一个元组。因为F#具有模式匹配,所以你真的不需要输出参数,因为有更好的方法可以从函数中返回多个值。

所以,这样的事情会更加惯用

let (value, success) = instance.GetValue(0x10)

其中instance.GetValue是

unit -> ('a, bool) 

或者您可以返回一个选项并执行类似

的操作
match instance.GetValue(0x10) with
| Some value -> doStuff value
| None -> failwith "Oops!"

答案 2 :(得分:5)

您必须使用reference cell

let success = ref false
instance.GetValue(0x10, success)

// access the value
!success

答案 3 :(得分:1)

我认为这里也值得一提的是,out参数的值不必初始化。

可以执行以下操作:

let mutable success3 = Unchecked.defaultof<bool>
let result3 = o.GetValue (10, &success3)

在您使用数组作为输出参数调用.NET库函数的情况下,这可能很有用,即:

let mutable currFeatures = Unchecked.defaultof<PointF[]>
let mutable status = Unchecked.defaultof<byte[]>
let mutable trackError = Unchecked.defaultof<float32[]>

CvInvoke.CalcOpticalFlowPyrLK(
      previousFrame, 
      nextFrame, 
      previousPoints, 
      Size(15,15), 
      2, 
      MCvTermCriteria(10, 0.03), 
      //Out params 
      &currFeatures, 
      &status, 
      &trackError,
      //---------
      LKFlowFlag.UserInitialFlow)