更具体地说,委托具有作为 F# 中的第一类值所没有的功能的特征(如果有的话)是什么?C# 中的委托没有哪些作为一流值起作用的特征(如果有的话)?
2 回答
Delegates 和 F#“First class function values”是完全不同的。
委托是 CLR 的一种机制,是函数-指针+对象对的类型安全包装器(例如方法,-指针this
与方法地址一起被捕获)。
另一方面,F# 函数值是抽象类的实现(它曾经在 F# 正式发布之前FSharpFunc<,>
被调用)。FastFunc<,>
调用通过普通的虚拟方法进行,这比委托调用要快得多。这就是 F# 团队一开始没有使用委托的原因。
因此,如果您可以通过抽象类/虚拟方法将函数“实现”为第一类值,那么微软为什么要添加委托呢?
- 没有其他选择在 .NET 1.0/1.1 中,没有泛型,因此您必须为要使用的每个函数签名定义一个新的委托类型 (="function type")。
- (不,仅使用 Java 中的接口不算数。:-P)
好的,但是我们从 .NET 2.0 开始就有泛型,为什么我们仍然有委托?为什么我们不能只使用Func<,>
andAction<>
来做所有事情?
- 向后兼容性
- 多播代表代表可以链接在一起形成新的代表。此机制用于在 VB.NET 和 C# 中实现事件。在幕后,一个事件实际上只是一个委托字段。使用
+=
语法,您基本上将您的事件处理程序委托添加到事件字段中的委托链中。
除了事件之外,是否有理由使用委托FSharpFunc<,>
FSharpFunc<,>
是的,一个:包含 lambda-expressions* 的每个实现都是一个新类。在 .NET 中,类被编码在已编译程序集的元数据中。另一方面,代表不需要额外的元数据。委托类型可以,但实例化这些委托类型在元数据方面是免费的。
但是等等,C# lambda 表达式/匿名方法不是也被实现为隐藏类吗?
是的,C# lambda 是两全其美的 ^^
我只是想补充一点,SealedSun 的这句话是不正确的:
调用通过普通的虚拟方法进行,这比委托调用要快得多。这就是 F# 团队一开始没有使用委托的原因。
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<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
在我的电脑上产生以下结果
delegate call: 65ms
fsharp func (staticly linked): 4ms
fsharp func (dynamic invoke): 356ms