Интеграция C# и F#

    • WinForms
    • .NET
    • C#
    • F#
  • modified:
  • reading: 3 minutes

Перед использованием F# задаешься вопросом, а можно ли использовать функции F# в приложениях, написанных на C#.

Ознакомиться с функциональным языком F# можно, к примеру, по этому адресу: http://msdn.microsoft.com/ru-ru/magazine/cc164244.aspx

Но вот, по моему мнению, F# как функциональный язык хорош для написания различных математических функций (извиняюсь за тавтологию), но зачем отбирать хлеб у объектно-ориентированных языков, зачем городить непонятный код для работы с WinForms контролами или WebForms страницами на F#? Потому я сразу же задался вопросом, как вызывать функции из F# сборок. Сразу хочу сказать, что так как в функциональных языках есть сложности с типизациями при написании кода, то в случае использования F# функций из сборок C# эти трудности только увеличиваются. Приступим к делу.

Создаем проект, который включает в себя, к примеру, C# консольное приложение и сборку F#.

В сборке F# нам нужен один файл MyFunctions.fs. Там мы описываем некоторые функции, которые по нашему мнению нам проще написать на функциональном языке. К примеру, пускай это будет функция перевода массива битовой картинки из цветового пространства RGB в YCbCr (это просто пример). Запись в F# может быть примерно такая:

open System
// Перевод в цветовое пространство YCbCr
let RGB_to_YCbCr (r : double,g : double,b : double) = 
    let y = 0.299 * r + 0.587 * g + 0.114 * b in
    let Cb = (-0.1687) * r - 0.3313 * g + 0.5 * b + 128.0 in
    let Cr = 0.5 * r - 0.4187 * g - 0.0813 * b + 128.0 in
    (y,Cb,Cr);
   
let RGB_to_YCbCr_v (v : _ array) = 
    RGB_to_YCbCr (v.[0], v.[1], v.[2]);
let Process (image : _ array)  = 
    let lenght = Array.length image in
    let imageYCbCr = Array.create lenght (0.0, 0.0, 0.0) in
    for index = 0 to lenght - 1 do
        imageYCbCr.[index] <- RGB_to_YCbCr_v (image.[index])
    done
    imageYCbCr

После сборки мы можем заметить, что добраться до функций не просто, namespace имеют непонятный вид, и как их использовать тоже не представляется ясным. Посмотреть расположение функций в сборке мы можем при помощи Reflector.

Для того, чтобы описать namespace и класс необходимо добавить следующую строку сразу после #light:

module FSharp.Sample.MyFunctions

Которая говорит, о том что все функции написанные ниже будут содержаться в классе MyFunctions пространства имен FSharp.Sample.

Собрав проект еще раз, мы увидим, что в сборке есть четкий namespace  FSharp.Sample, который имеет класс MyFunctions со статическими методами, которые мы описали выше.

Дальше в нашем консольном приложении мы устанавливаем Reference на сборки FSharp.Sample - имя моей сборки на F#, и FSharp.Core - для того, чтобы использовать типы (классы) F# типа Triple. И пишем следующий код:

using System;
using FSharp.Sample;
namespace CSharp.Sample.Console
{
    class Program
    {
        static void Main()
        {
            double[][] image = new double[1000][];
            Random rand = new Random();
            for (int i = 0; i < 1000; i ++ )
            {
                image[i] = new double[3];
                image[i][0] = rand.Next();
                image[i][1] = rand.Next();
                image[i][2] = rand.Next();
            }
            foreach (var doubles in MyFunctions.Process(image))
            {
                System.Console.WriteLine(doubles);
            } 
        }
    }
}

Где изначально мы указываем на использование namespace FSharp.Sample. В коде мы генерируем массив данных и передаем его в функцию MyFunction.Process, которая преобразовывает нам его по выбранному алгоритму. Данные нам возвращаются в виде массива данных типов "Microsoft.FSharp.Core.Tuple`3".

See Also