Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Расширения типов (также называемые расширениями) — это семейство функций, которые позволяют добавлять новые элементы в ранее определенный тип объекта. Ниже приведены три функции.
- Расширения встроенных типов
- Дополнительные расширения типов
- Методы расширения
Каждый из них может использоваться в разных сценариях и имеет разные компромиссы.
Синтаксис
// Intrinsic and optional extensions
type typename with
member self-identifier.member-name =
body
...
// Extension methods
open System.Runtime.CompilerServices
[<Extension>]
type Extensions() =
[<Extension>]
static member extension-name (ty: typename, [args]) =
body
...
Расширения встроенных типов
Расширение встроенного типа — это расширение типа, которое расширяет определяемый пользователем тип.
Расширения встроенных типов должны быть определены в том же файле и в том же пространстве имен или модуле, что и расширение типа. Любое другое определение приведет к тому, что они будут необязательными расширениями типов.
Расширения встроенных типов иногда являются более понятным способом разделения функциональных возможностей из объявления типа. В следующем примере показано, как определить расширение встроенного типа:
namespace Example
type Variant =
| Num of int
| Str of string
module Variant =
let print v =
match v with
| Num n -> printf "Num %d" n
| Str s -> printf "Str %s" s
// Add a member to Variant as an extension
type Variant with
member x.Print() = Variant.print x
Использование расширения типа позволяет разделить каждое из следующих элементов:
- Объявление
Variantтипа - Функции печати
Variantкласса в зависимости от его "фигуры" - Способ доступа к функциям печати с помощью нотации в стиле
.объектов
Это альтернатива определению всего как члена Variant. Хотя это не по сути лучший подход, это может быть более чистое представление функциональных возможностей в некоторых ситуациях.
Встроенные расширения типов компилируются как члены типа, который они расширяют, и отображаются на типе, когда тип проверяется отражением.
Дополнительные расширения типов
Дополнительное расширение типа — это расширение, которое отображается вне исходного модуля, пространства имен или сборки расширенного типа.
Дополнительные расширения типов полезны для расширения типа, который вы не определили самостоятельно. Рассмотрим пример.
module Extensions
type IEnumerable<'T> with
/// Repeat each element of the sequence n times
member xs.RepeatElements(n: int) =
seq {
for x in xs do
for _ in 1 .. n -> x
}
Теперь вы можете получить доступ RepeatElements , как если это член IEnumerable<T> , если Extensions модуль открыт в области, в которую вы работаете.
Необязательные расширения не отображаются в расширенном типе при проверке отражением. Необязательные расширения должны находиться в модулях, и они доступны только в том случае, если модуль, содержащий расширение, открыт или находится в области.
Необязательные члены расширения компилируются в статические элементы, для которых экземпляр объекта передается неявно в качестве первого параметра. Однако они действуют так, как будто они являются членами экземпляра или статическими элементами в соответствии с тем, как они объявлены.
Необязательные члены расширения также не видны потребителям C# или Visual Basic. Их можно использовать только в другом коде F#.
Универсальное ограничение встроенных и необязательных расширений типов
Можно объявить расширение типа в универсальном типе, где переменная типа ограничена. Требование заключается в том, что ограничение объявления расширения соответствует ограничению объявленного типа.
Однако даже если ограничения совпадают между объявленным типом и расширением типа, можно определить ограничение текстом расширенного элемента, которое накладывает другое требование к параметру типа, чем объявленный тип. Рассмотрим пример.
open System.Collections.Generic
// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
member this.Sum() = Seq.sum this
Невозможно получить этот код для работы с дополнительным расширением типа:
- Как и в случае, член имеет другое ограничение (
static member get_Zero'Tиstatic member (+)) по сравнению с тем,Sumчто определяет расширение типа. - Изменение расширения типа таким же ограничением, что
Sumи определенное ограничениеIEnumerable<'T>. -
member inline this.SumИзменениеmember this.Sumприсвоит ошибке несоответствие ограничений типов.
То, что нужно, являются статическими методами, которые "плавают в пространстве" и могут быть представлены так, как если бы они расширяли тип. Здесь необходимы методы расширения.
Методы расширения
Наконец, методы расширения (иногда называемые элементами расширения стиля C#) можно объявить в F# как статический метод-член класса.
Методы расширения полезны, если вы хотите определить расширения в универсальном типе, который будет ограничивать переменную типа. Рассмотрим пример.
namespace Extensions
open System.Collections.Generic
open System.Runtime.CompilerServices
[<Extension>]
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
При использовании этот код будет отображаться так, как если Sum бы он был определен IEnumerable<T>, если Extensions он был открыт или находится в области.
Чтобы расширение было доступно для VB.NET кода, необходимо дополнительное ExtensionAttribute значение на уровне сборки:
module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()
Другие замечания
Расширения типов также имеют следующие атрибуты:
- Любой тип, к которому можно получить доступ, может быть расширен.
- Встроенные и необязательные расширения типов могут определять любой тип элемента, а не только методы. Поэтому свойства расширения также возможны, например.
- Маркер
self-identifierв синтаксисе представляет экземпляр вызываемого типа, как и обычные члены. - Расширенные члены могут быть статическими или экземплярами.
- Переменные типа в расширении типа должны соответствовать ограничениям объявленного типа.
Для расширений типов также существуют следующие ограничения:
- Расширения типов не поддерживают виртуальные или абстрактные методы.
- Расширения типов не поддерживают переопределение методов в качестве расширений.
- Расширения типов не поддерживают статически разрешенные параметры типа.
- Необязательные расширения типов не поддерживают конструкторы в качестве расширений.
- Расширения типов нельзя определить для аббревиаций типа.
- Расширения типов недопустимы
byref<'T>(хотя их можно объявить). - Расширения типов недопустимы для атрибутов (хотя их можно объявить).
- Вы можете определить расширения, которые перегружают другие методы того же имени, но компилятор F# предпочтительнее использовать методы, не являющиеся расширениями, если есть неоднозначный вызов.
Наконец, если для одного типа существуют несколько расширений встроенных типов, все члены должны быть уникальными. Для расширений необязательных типов члены в разных расширениях типов для одного типа могут иметь одинаковые имена. Ошибки неоднозначности возникают только в том случае, если клиентский код открывает две разные области, определяющие одинаковые имена элементов.