繁体   English   中英

为什么在F#中使用默认参数(FSharpOption <T> )是引用类型?

[英]Why default arguments in F# (FSharpOption<T>) are reference types?

C#和F#对默认(或可选)参数的实现不同。

在C#语言中,当您向参数添加默认值时,您不会更改其基础类型(我是指参数的类型)。 实际上,C#中的可选参数是一个轻量级的语法糖:

class CSharpOptionalArgs
{
  public static void Foo(int n = 0) {}
}

// Somewhere in the call site

CSharpOptionalArgs.Foo();
// Call to Foo() will be transformed by C# compiler
// *at compile time* to something like:
const int nArg = GetFoosDefaultArgFromTheMetadata();
CSharpOptionalArgs.Foo(nArg);

但是F#以不同的方式实现此功能。 与C#不同,F#可选参数在被调用者站点而不是在调用者站点解析:

type FSharpOptionalArgs() =
    static let defaultValue() = 42

    static member public Foo(?xArg) =
        // Callee site decides what value to use if caller does not specifies it
        let x = defaultArg xArg (defaultValue())
        printfn "x is %d" x

这种实现是绝对合理的,并且功能更强大。 C#可选参数仅限于编译时常量(因为可选参数存储在程序集元数据中)。 在F#中,默认值可能不太明显,但是我们可以使用任意表达式作为默认值。 到目前为止,一切都很好。

F#编译器将F#可选参数转换为作为引用类型的 Microsoft.FSharp.Core.FSharpOption<'a> 这意味着对F#中带有可选参数的方法的每次调用都将导致在托管头进行额外的分配,并给垃圾回收带来压力

**EDITED**
// This call will NOT lead to additional heap allocation!
FSharpOptionalArgs.Foo()
// But this one will do!
FSharpOptionalArgs.Foo(12)

我不用担心应用程序代码,但是这种行为可能会大大降低库的性能。 如果某个带有可选参数的库方法每秒被调用数千次怎么办?

这种实现对我来说真的很奇怪。 但是也许有一些规则库开发人员应避免使用此功能,否则F#团队将在将来的F#版本中更改此行为?

遵循单元测试,表明可选参数是引用类型:

[<TestFixture>]
type FSharpOptionalArgumentTests() =

    static member public Foo(?xArg) =
        // Callee site decides what value to use if caller does not specifies it
        let x = defaultArg xArg 42
        ()

    [<Test>]
    member public this.``Optional argument is a reference type``() =
        let pi = this.GetType().GetMethod("Foo").GetParameters() |> Seq.last

        // Actually every optional parameter in F# is a reference type!!
        pi.ParameterType |> should not' (equal typeof<int>)
        pi.ParameterType.IsValueType |> should be False
        ()

因为F#团队中没有人对编译“简单”感兴趣,所以像Option联合区分为值类型,支持在此类联合上进行模式匹配等:)

请记住,元组类型在F#等功能语言中大量使用(远远超过默认参数),但仍在CLR中作为引用类型实现-没有人关心内存分配和特定于函数语言的GC调整。

F#4.1具有C#可选属性兼容性。 但是,如果您正在开发也会被F#使用的函数,则应使用常规语法:

member this.M (?i : int) = let iv = defaultArg i 12 iv + 1

F#4.1可选参数文档: https : //docs.microsoft.com/zh-cn/dotnet/fsharp/language-reference/members/methods

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM