Более конкретно, каковы характеристики (если они есть), которые у делегатов имеют такие функции, как значения первого класса в F #, не имеют; и каковы характеристики, которые функционируют как значения первого класса, имеют (если они есть), которые делегаты на С# не имеют?
В чем разница между делегатами в С# и функциями как значения первого класса в F #?
Ответ 1
Делегаты и F # "Значения функций первого класса" совершенно разные.
Делегаты - это механизм CLR, защищенная от типа оболочка вокруг пар-указателей + пар объектов (например, методы, this
-pointer захватываются вместе с адресом метода).
Значения функции F #, с другой стороны, представляют собой реализацию абстрактного класса FSharpFunc<,>
(его раньше называли FastFunc<,>
до официального выпуска F #). Вызов происходит через обычные виртуальные методы, которые намного быстрее, чем вызов делегата.
Именно по этой причине F # -team не использовал делегатов в первую очередь.
Итак, если вы можете "реализовать" функции как значения первого класса через абстрактные классы/виртуальные методы, почему Microsoft добавила делегатов?
- В .NET 1.0/1.1 не было альтернативы. Генералов не было, поэтому вам нужно было определить новый тип делегата (= "тип функции" ) для каждой сигнатуры функции, которую вы хотели использовать,
- (Нет, просто использование интерфейсов, таких как Java, не учитывается.: -P)
Хорошо, но у нас есть Generics с .NET 2.0, почему у нас все еще есть делегаты? Почему мы не можем просто использовать Func<,>
и Action<>
для всего?
- Обратная совместимость
- Многоадресные делегаты. Делегаты могут быть соединены вместе, чтобы сформировать новых делегатов. Этот механизм используется для реализации событий в VB.NET и С#. За кулисами событие - это всего лишь одно поле для делегатов. Используя синтаксис
+=
, вы по существу добавляете делегат-обработчик событий в цепочку делегатов в поле события.
Помимо событий, есть ли причина использовать делегатов над FSharpFunc<,>
Да, один: каждая реализация FSharpFunc<,>
, включающая лямбда-выражения *, является новым классом. А в .NET классы кодируются в метаданных скомпилированной сборки. С другой стороны, делегаты не требуют дополнительных метаданных. Типы делегатов, но создание экземпляров этих типов делегатов является бесплатным с точки зрения метаданных.
Но подождите, не являются ли выражения лямбда-выражения С#/анонимные методы, реализованные как скрытые классы?
Да, С# lambdas берут худшее из обоих миров ^^
Ответ 2
Я просто хотел добавить, что это утверждение из 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<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