#f# #class-library #c#-to-f#
#f# #class-library #c #-to-f#
Вопрос:
У меня есть сборка библиотеки классов F #, которая содержит две функции:
let add a b = a b
и
let rec aggregateList list init (op:int -> int -> int) =
match list with
|[] -> init
|head::tail ->
let rest = aggregateList tail init op
op rest head
У меня есть консольное приложение C #, которое ссылается на библиотеку F # и пытается выполнить следующее:
FSharpList<int> l = new FSharpList<int>(1, new FSharpList<int>(2, FSharpList<int>.Empty));
int result = myFsLibrary.aggregateList(l, 0, myFsLibrary.add);
Однако компилятор жалуется, что [myFsLibrary.add] не может быть преобразован из ‘method group’ в FSharpFunc<int, FSharpFunc<int, int>>
Ответ №1:
Другие люди предоставили ответы, но я просто вмешаюсь, чтобы сказать, что вам не следует этого делать.
Не предоставляйте списки F # C #. Не предоставляйте C # функции curried. Несоответствие импеданса видно на этой границе, поэтому лучше предоставлять общие типы фреймворка на границах межъязыковой сборки. Смотрите
для получения дополнительных советов.
Комментарии:
1. Почему бы и нет? Это одна из рекомендаций, которую я не понимаю. Если я хочу иметь гарантии неизменяемых коллекций, не является ли F # List единственной доступной вещью, которая обеспечивает неизменность в .NET? Если кто-то использует IEnumerable, это не дает никаких гарантий неизменности чего-либо для клиента библиотеки. Я только хотел бы, чтобы FSharpList был интерфейсом…
Ответ №2:
Вы можете явно создать функцию, используя FSharpFunc
делегат. В C # удобнее создать функцию, которая принимает все аргументы в виде кортежа, так что вы можете сделать это, а затем преобразовать функцию в тип curried с помощью FuncConvert
. Что-то вроде:
FuncConvert.FuncFromTupled(new FSharpFunc<Tuple<int, int>, int>(args =>
arags.Item1 args.Item2))
Однако, если вам нужно вызвать какую-либо функцию F # из вашего кода на C #, рекомендуется предоставить функцию с интерфейсом, удобным для C #. В этом случае я могу использовать Func
делегат, и первый аргумент должен быть IEnumerable
вместо типа списка, специфичного для F #:
module List =
let AggregateListFriendly inp init (op:Func<int, int, int>) =
aggregateList (List.ofSeq inp) init (fun a b -> op.Invoke(a, b))
Тогда ваше приложение C # может просто использовать:
List.AggregateListFriendly(Enumerable.Range(0, 10), 0, (a, b) => a b));
Ответ №3:
Причина в том, что add экспортируется как обычный .Функция в сетевом стиле и имеет грубую подпись
int add(int, int)
C # и большинство других.Сетевые языки рассматривают это как метод, который принимает 2 int
параметра и возвращает единственное int
значение. Однако F # не видит функции таким образом. Вместо этого он видит, add
как функция принимает int
и возвращает функцию, которая, в свою очередь, принимает int
и возвращает int
. Такой вид функций позволяет очень легко реализовать такие операции, как каррирование.
Чтобы преобразовать представление мира C # в F #, вам нужно немного поколдовать, чтобы наложить метод на себя. Я добиваюсь этого, определяя набор методов F # factory и extension, которые творят чудеса за меня. Например
[<Extension>]
type public FSharpFuncUtil =
[<Extension>]
static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) =
fun x y -> func.Invoke(x,y)
static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) =
FSharpFuncUtil.ToFSharpFunc func
Я могу использовать эту библиотеку, чтобы получить соответствующий тип делегата F # для add
метода следующим образом
var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);