C#中的委托与F#中的第一类值有什么区别?

更具体地说,代表具有作为F#中的第一类值的函数的特征(如果有的话)没有; 作为第一类值的特征有什么特征(如果有的话)在C#中委托没有?

代表和F#“头等function值”是完全不同的。

委托是CLR的一种机制,它是一个围绕函数指针+对象对的类型安全包装器(例如方法, this -pointer与方法地址一起被捕获)。

另一方面,F#函数值是抽象类FSharpFunc<,> (它曾经在F#正式发布之前被称为FastFunc<,> )。 调用通过普通的虚方法发生,这比委托调用快得多。 这就是F#-team最初没有使用代表的原因。

因此,如果您可以通过抽象类/虚方法“实现”作为第一类值的函数,为什么Microsoft会添加委托?

  • 没有其他选择在.NET 1.0 / 1.1中,没有generics,因此您必须为要使用的每个函数签名定义新的委托类型(=“函数类型”)。
  • (不,只使用Java中的接口不算数。:-P)

好的,但是自.NET 2.0以来我们有Generics,为什么我们还有代表呢? 为什么我们不能只使用Func<,>Action<>来做所有事情?

  • 向后兼容性
  • 多播委托代表可以链接在一起以形成新的委托。 该机制用于在VB.NET和C#中实现事件。 在幕后,一个事件实际上只是一个代表字段。 使用+=语法,您实际上将event-handler-delegate添加到事件字段中的委托链中。

除了事件之外,是否有理由使用委托而不是FSharpFunc<,>

是的,一: FSharpFunc<,>每个实现,包括lambda-expressions *,都是一个新类。 在.NET中,类编译在已编译程序集的元数据中。 另一方面,代表不需要额外的元数据。 委托类型可以,但实例化这些委托类型在元数据方面是免费的。

但是等等,C#lambda-expressions /匿名方法是不是实现为隐藏类?

是的,C#lambdas占据了两个世界中最糟糕的^^

我只想补充一点,SealedSun的这句话不是真的:

调用通过普通的虚方法发生,这比委托调用快得多。 这就是F#-team最初没有使用代表的原因。

F#函数并不比委托调用快,可能是.NET 1.0中的情况,但现在委托调用和调用虚拟方法几乎相当。

与调用委托相比,调用编译器静态绑定的F#函数也非常慢。

 open System open System.Diagnostics let time name f = let sw = new Stopwatch() sw.Start() f() sw.Stop() printfn "%s: %dms" name sw.ElapsedMilliseconds time "delegate call" ( fun () -> let f = new Func( fun i1 i2 -> let y = i1 + i2 let x = y + i1 let z = x + y + i2 z + x + y + i1 ) let mutable r = 0 for i = 0 to 10000000 do r <- f.Invoke(i, i) ) let f i1 i2 = let y = i1 + i2 let x = y + i1 let z = x + y + i2 z + x + y + i1 time "fsharp func (static bound)" ( fun () -> let mutable r = 0 for i = 0 to 10000000 do r <- fii ) let make f = let mutable r = 0 for i = 0 to 10000000 do r <- fii time "fsharp func (dynamic bound)" ( fun () -> make f ) Console.ReadLine() |> ignore 

在我的计算机上生成以下结果

 delegate call: 65ms fsharp func (staticly linked): 4ms fsharp func (dynamic invoke): 356ms