简体   繁体   English

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

[英]What is the difference between delegates in C# and functions as first class values in F#?

More specifically what are the characteristics (if any) that delegates have that functions as first class values in F# don't have; 更具体地说,代表具有作为F#中的第一类值的函数的特征(如果有的话)没有; and what are the characteristics that functions as first class values have (if any) that delegates in C# don't have? 作为第一类值的特征有什么特征(如果有的话)在C#中委托没有?

Delegates and F# "First class function values" are quite different. 代表和F#“一流的功能价值”是完全不同的。

Delegates are a mechanism of the CLR, a type-safe wrapper around function-pointer+object pairs (for instance methods, the this -pointer gets captured together with the method address). 委托是CLR的一种机制,它是一个围绕函数指针+对象对的类型安全包装器(例如方法, this -pointer与方法地址一起被捕获)。

F# function values on the other hand, are implementation of an abstract class FSharpFunc<,> (it used to be called FastFunc<,> before the official release of F#). 另一方面,F#函数值是抽象类FSharpFunc<,> (它曾经在F#正式发布之前被称为FastFunc<,> )。 Invocation happens via ordinary virtual methods, which is much faster than delegate invocation. 调用通过普通的虚方法发生,这比委托调用快得多。 That is the reason the F#-team didn't use delegates in the first place. 这就是F#-team首先不使用委托的原因。

So if you can "implement" functions as first class values via abstract classes/virtual methods, why did Microsoft add delegates? 因此,如果您可以通过抽象类/虚方法“实现”作为第一类值的函数,为什么Microsoft会添加委托?

  • There was no alternative In .NET 1.0/1.1, there were no generics, so you had to define a new delegate type (="function type") for every function signature you wanted to use. 没有其他选择在.NET 1.0 / 1.1中,没有泛型,因此您必须为要使用的每个函数签名定义新的委托类型(=“函数类型”)。
  • (No, just using interfaces like in Java doesn't count. :-P ) (不,只是使用Java中的接口不计算。:-P)

Ok, but we have Generics since .NET 2.0, why do we still have delegates? 好的,但是从.NET 2.0开始我们有Generics,为什么我们还有代表呢? Why can't we just use Func<,> and Action<> for everything? 为什么我们不能只使用Func<,>Action<>来做所有事情?

  • Backwards compatibility 向后兼容性
  • Multicast Delegates Delegates can be chained together to form new delegates. 多播代理可以将代理链接在一起以形成新的代理。 This mechanism is used to implement events in VB.NET and C#. 此机制用于在VB.NET和C#中实现事件。 Behind the scenes, an event is really just a single delegate field. 在幕后,一个事件实际上只是一个代表字段。 Using the += syntax you essentially add your event-handler-delegate to the chain of delegates in the event field. 使用+=语法,您实际上将事件处理程序委托添加到事件字段中的委托链。

Apart from events, is there a reason to use delegates over FSharpFunc<,> 除了事件,是否有理由使用委托而不是FSharpFunc<,>

Yes, one: Each and every implementation of FSharpFunc<,> , that includes lambda-expressions*, is a new class. 是的,一: FSharpFunc<,>每个实现,包括lambda-expressions *,都是一个新类。 And in .NET classes are encoded in the metadata of the compiled assembly. 在.NET中,类编译在已编译程序集的元数据中。 Delegates on the other hand require no extra metadata. 另一方面,代表不需要额外的元数据。 The delegate types do but instantiating these delegate types is free in terms of metadata. 委托类型可以,但实例化这些委托类型在元数据方面是免费的。

But wait, aren't C# lambda-expressions/anonymous methods too implemented as hidden classes? 但是等等,C#lambda-expressions /匿名方法是不是实现为隐藏类?

Yes, C# lambdas take the worst of both worlds ^^ 是的,C#lambdas占据了两个世界中最糟糕的^^

I just wanted to add that this statement from SealedSun isn't true: 我只是想补充说SealedSun的这句话不是真的:

Invocation happens via ordinary virtual methods, which is much faster than delegate invocation. 调用通过普通的虚方法发生,这比委托调用快得多。 That is the reason the F#-team didn't use delegates in the first place. 这就是F#-team首先不使用委托的原因。

F# functions are not faster then delegate invocation, maybe that was the case back in .NET 1.0, but now a days delegate invocation and invoking virtual methods are pretty much on-par. F#函数并不比委托调用快,可能是.NET 1.0中的情况,但现在委托调用和调用虚拟方法几乎相当。

Also invoking F# functions that can't be bound statically by the compiler is very slow compared to invoking a delegate. 与调用委托相比,调用编译器静态绑定的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<int, int, int>(
        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 <- f i i
)

let make f =
  let mutable r = 0
  for i = 0 to 10000000 do
    r <- f i i

time "fsharp func (dynamic bound)" (
  fun () -> make f
)

Console.ReadLine() |> ignore

Produces the following results on my computer 在我的计算机上生成以下结果

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

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

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