Поделиться через


Интерфейсы (F#)

Интерфейсы указывают наборы связанных элементов, которые реализуют другие классы.

Синтаксис

// Interface declaration:
[ attributes ]
type [accessibility-modifier] interface-name =
    [ interface ]     [ inherit base-interface-name ...]
    abstract member1 : [ argument-types1 -> ] return-type1
    abstract member2 : [ argument-types2 -> ] return-type2
    ...
[ end ]

// Implementing, inside a class type definition:
interface interface-name with
    member self-identifier.member1argument-list = method-body1
    member self-identifier.member2argument-list = method-body2

// Implementing, by using an object expression:
[ attributes ]
let class-name (argument-list) =
    { new interface-name with
        member self-identifier.member1argument-list = method-body1
        member self-identifier.member2argument-list = method-body2
        [ base-interface-definitions ]
    }
    member-list

Замечания

Объявления интерфейса похожи на объявления классов, за исключением того, что элементы не реализованы. Вместо этого все члены абстрактны, как указано ключевое словоabstract. Текст метода для абстрактных методов не предоставляется. F# не может определить реализацию метода по умолчанию в интерфейсе, но она совместима с реализациями по умолчанию, определенными C#. Реализации по умолчанию, использующие default ключевое слово, поддерживаются только при наследовании от базового класса, отличного от интерфейса.

Специальные возможности по умолчанию для интерфейсов public.

При необходимости можно предоставить каждому параметру метода имя с помощью обычного синтаксиса F#:

type ISprintable =
    abstract member Print: format: string -> unit

В приведенном выше ISprintable примере Print метод имеет один параметр типа string с именем format.

Существует два способа реализации интерфейсов: с помощью выражений объектов и с помощью типов. В любом случае выражение типа или объекта предоставляет тела методов для абстрактных методов интерфейса. Реализации зависят от каждого типа, реализующего интерфейс. Поэтому методы интерфейса для разных типов могут отличаться друг от друга.

Ключевое слово interface и end, которые помечают начало и конец определения, являются необязательными при использовании упрощенного синтаксиса. Если эти ключевое слово не используются, компилятор пытается определить, является ли тип классом или интерфейсом, анализируя используемые конструкции. Если вы определяете член или используете другой синтаксис класса, тип интерпретируется как класс.

Стиль программирования .NET заключается в том, чтобы начать все интерфейсы с буквой.I

Можно указать несколько параметров двумя способами: F#-style и . Стиль NET. Оба будут компилировать одинаковый способ для потребителей .NET, но F#-style приведет к тому, что вызывающие F# будут использовать приложение параметров F#и . В стиле NET вызовет вызовы F#, чтобы использовать приложение аргументов кортежей.

type INumericFSharp =
    abstract Add: x: int -> y: int -> int

type INumericDotNet =
    abstract Add: x: int * y: int -> int

Реализация интерфейсов с помощью типов классов

Вы можете реализовать один или несколько интерфейсов в типе класса с помощью interface ключевое слово, имени интерфейса и with ключевое слово, а затем определения элементов интерфейса, как показано в следующем коде.

type IPrintable =
    abstract member Print: unit -> unit

type SomeClass1(x: int, y: float) =
    interface IPrintable with
        member this.Print() = printfn "%d %f" x y

Реализации интерфейса наследуются, поэтому любые производные классы не нуждаются в их повторном выполнении.

Методы интерфейса вызова

Методы интерфейса можно вызывать только через интерфейс, а не через любой объект типа, реализующего интерфейс. Таким образом, может потребоваться переадресация к типу интерфейса с помощью :> оператора или upcast оператора для вызова этих методов.

Чтобы вызвать метод интерфейса при наличии объекта типа SomeClass, необходимо переадресовать объект в тип интерфейса, как показано в следующем коде.

let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()

Альтернативой является объявление метода в объекте, который переадресовывает и вызывает метод интерфейса, как показано в следующем примере.

type SomeClass2(x: int, y: float) =
    member this.Print() = (this :> IPrintable).Print()

    interface IPrintable with
        member this.Print() = printfn "%d %f" x y

let x2 = new SomeClass2(1, 2.0)
x2.Print()

Реализация интерфейсов с помощью выражений объектов

Выражения объектов предоставляют короткий способ реализации интерфейса. Они полезны, если вам не нужно создавать именованный тип, и требуется только объект, поддерживающий методы интерфейса без каких-либо дополнительных методов. Выражение объекта иллюстрируется в следующем коде.

let makePrintable (x: int, y: float) =
    { new IPrintable with
        member this.Print() = printfn "%d %f" x y }

let x3 = makePrintable (1, 2.0)
x3.Print()

Наследование интерфейса

Интерфейсы могут наследоваться от одного или нескольких базовых интерфейсов.

type Interface1 =
    abstract member Method1: int -> int

type Interface2 =
    abstract member Method2: int -> int

type Interface3 =
    inherit Interface1
    inherit Interface2
    abstract member Method3: int -> int

type MyClass() =
    interface Interface3 with
        member this.Method1(n) = 2 * n
        member this.Method2(n) = n + 100
        member this.Method3(n) = n / 10

Реализация интерфейсов с реализациями по умолчанию

C# поддерживает определение интерфейсов с реализацией по умолчанию, например:

using System;

namespace CSharp
{
    public interface MyDim
    {
        public int Z => 0;
    }
}

Они используются напрямую из F#:

open CSharp

// You can implement the interface via a class
type MyType() =
    member _.M() = ()

    interface MyDim

let md = MyType() :> MyDim
printfn $"DIM from C#: %d{md.Z}"

// You can also implement it via an object expression
let md' = { new MyDim }
printfn $"DIM from C# but via Object Expression: %d{md'.Z}"

Вы можете переопределить реализацию по умолчанию, overrideнапример переопределить любой виртуальный член.

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

Реализация одного и того же интерфейса в разных универсальных экземплярах

F# поддерживает реализацию одного и того же интерфейса в разных универсальных экземплярах, например:

type IA<'T> =
    abstract member Get : unit -> 'T

type MyClass() =
    interface IA<int> with
        member x.Get() = 1
    interface IA<string> with
        member x.Get() = "hello"

let mc = MyClass()
let iaInt = mc :> IA<int>
let iaString = mc :> IA<string>

iaInt.Get() // 1
iaString.Get() // "hello"

См. также