ООП и взаимодействие с .NET

F# функциональный, но не догматичный: когда нужно, он говорит на языке ООП и всей платформы .NET.

Интероперабельность с .NET — способность F# напрямую использовать любые типы, классы и библиотеки экосистемы .NET, написанные на C# или VB.NET.

Доступ к библиотекам .NET

Поскольку F# компилируется в тот же IL, ему доступна вся стандартная библиотека .NET и любой пакет NuGet — без обёрток.

open System

let now = DateTime.Now
let upper = "привет".ToUpper()
let rounded = Math.Round(3.14159, 2)
printfn "%s %f" upper rounded

Вывод:

ПРИВЕТ 3.140000

open System подключает пространство имён (как using в C#). Методы объектов вызывают через точку — привычный .NET-синтаксис.

Классы, когда они уместны

Для взаимодействия с ООП-кодом или инкапсуляции состояния F# умеет объявлять классы с конструктором, полями и методами.

type Counter(start: int) =
    let mutable count = start
    member this.Increment() = count <- count + 1
    member this.Value = count

let c = Counter(10)
c.Increment()
c.Increment()
printfn "%d" c.Value

Вывод:

12

Аргументы в скобках после имени типа — это первичный конструктор. member объявляет методы и свойства; this — ссылка на экземпляр.

Интерфейсы

F# реализует интерфейсы .NET — это нужно, например, чтобы тип работал с C#-кодом, ожидающим интерфейс.

type IGreeter =
    abstract member Greet: string -> string

type Friendly() =
    interface IGreeter with
        member this.Greet name = sprintf "Привет, %s!" name

let g = Friendly() :> IGreeter
printfn "%s" (g.Greet "Аня")

Вывод:

Привет, Аня!

Оператор :> — это восходящее приведение (upcast) к интерфейсу.

Когда использовать ООП в F#

Здравая стратегия: ядро логики — в функциональном стиле (записи, DU, функции), а классы и интерфейсы — на границах: для интеропа с C#, реализации фреймворковых интерфейсов, инкапсуляции изменяемого ресурса. Не стоит писать весь F# в ООП-стиле — теряется его сила.

Как работает под капотом

F#-типы — это полноценные типы .NET. Запись для C# выглядит как класс с свойствами, DU — как иерархия классов, модуль — как статический класс. Поэтому интероп двусторонний: C# видит F#-код как обычные .NET-типы, а F# видит C#-библиотеки напрямую. Это снимает барьер между языками внутри одного решения.

Частые ошибки

  • Писать на F# как на C# (классы вместо записей и DU) — теряется выразительность.
  • Забывать open для пространства имён перед использованием его типов.
  • Путать восходящее приведение :> (безопасное, к базовому/интерфейсу) и нисходящее :?> (рискованное).

Итоги

  • F# напрямую использует всю экосистему .NET: stdlib, NuGet, типы C#.
  • При нужде объявляет классы (первичный конструктор, member) и реализует интерфейсы.
  • Идиома: функциональное ядро, ООП — на границах для интеропа и инкапсуляции.
  • F#-типы — полноценные .NET-типы, поэтому интероп с C# двусторонний.
Проверьте себя
1. Почему F# может напрямую использовать библиотеки C#?
AF# транслируется в C#
BОба компилируются в IL и работают на CLR
CF# копирует исходники C#
DЭто невозможно
2. Что объявляют аргументы в скобках после имени типа: type Counter(start: int)?
AМетод
BПервичный конструктор
CИнтерфейс
DКортеж
3. Какова рекомендуемая идиома сочетания ФП и ООП в F#?
AВсё писать классами
BФункциональное ядро, ООП — на границах (интероп, интерфейсы)
CНикогда не использовать классы
DООП для всех вычислений