型拡張Type extensions

型拡張機能 ( _拡張_とも呼ばれます) は、以前に定義されたオブジェクト型に新しいメンバーを追加できるようにするための機能ファミリです。Type extensions (also called augmentations) are a family of features that let you add new members to a previously defined object type. 次の3つの機能があります。The three features are:

  • 組み込み型拡張機能Intrinsic type extensions
  • 省略可能な型拡張Optional type extensions
  • 拡張メソッドExtension methods

各は異なるシナリオで使用でき、トレードオフも異なります。Each can be used in different scenarios and has different tradeoffs.

構文Syntax

// 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 self-identifier.extension-name (ty: typename, [args]) =
        body
    ...

組み込み型拡張機能Intrinsic type extensions

組み込み型拡張は、ユーザー定義型を拡張する型の拡張機能です。An intrinsic type extension is a type extension that extends a user-defined type.

組み込み型拡張は、拡張する型と同じファイル 同じ名前空間またはモジュールで定義されている必要があります。Intrinsic type extensions must be defined in the same file and in the same namespace or module as the type they're extending. その他の定義では、 省略可能な型拡張になります。Any other definition will result in them being optional type extensions.

組み込み型の拡張機能は、型宣言から機能を分離するために、明確な方法である場合があります。Intrinsic type extensions are sometimes a cleaner way to separate functionality from the type declaration. 次の例は、組み込み型拡張を定義する方法を示しています。The following example shows how to define an intrinsic type extension:

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

型拡張を使用すると、次の各項目を分離できます。Using a type extension allows you to separate each of the following:

  • 型の宣言 VariantThe declaration of a Variant type
  • Variant"Shape" に応じてクラスを印刷する機能Functionality to print the Variant class depending on its "shape"
  • 印刷機能にオブジェクト形式の表記でアクセスする方法 .A way to access the printing functionality with object-style .-notation

これは、すべてをのメンバーとして定義する方法の1つです VariantThis is an alternative to defining everything as a member on Variant. これは本質的には優れたアプローチではありませんが、状況によっては機能が明確に表示される可能性があります。Although it is not an inherently better approach, it can be a cleaner representation of functionality in some situations.

組み込み型の拡張は、それが拡張する型のメンバーとしてコンパイルされ、リフレクションによって型が検証されるときに型に表示されます。Intrinsic type extensions are compiled as members of the type they augment, and appear on the type when the type is examined by reflection.

省略可能な型拡張Optional type extensions

省略可能な型拡張は、拡張される型の元のモジュール、名前空間、またはアセンブリの外側に表示される拡張機能です。An optional type extension is an extension that appears outside the original module, namespace, or assembly of the type being extended.

省略可能な型拡張は、自分で定義していない型を拡張する場合に便利です。Optional type extensions are useful for extending a type that you have not defined yourself. 次に例を示します。For example:

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 スコープでモジュールが開かれている限り、にアクセスできるようになりました。You can now access RepeatElements as if it's a member of IEnumerable<T> as long as the Extensions module is opened in the scope that you are working in.

リフレクションによってチェックされる場合、拡張型にはオプションの拡張機能は表示されません。Optional extensions do not appear on the extended type when examined by reflection. オプションの拡張機能はモジュール内に存在する必要があり、拡張機能を含むモジュールが開いている場合、またはそれ以外の場合はスコープ内にある場合にのみスコープに含まれます。Optional extensions must be in modules, and they're only in scope when the module that contains the extension is open or is otherwise in scope.

任意拡張のメンバーはコンパイルされると、静的メンバーになります。このメンバーに対する最初のパラメーターとして、オブジェクト インスタンスが暗黙で渡されます。Optional extension members are compiled to static members for which the object instance is passed implicitly as the first parameter. ただし、これらは、それらが宣言されている方法に従って、インスタンスメンバーまたは静的メンバーであるかのように動作します。However, they act as if they're instance members or static members according to how they're declared.

オプションの拡張メンバーは、C# または Visual Basic コンシューマーにも表示されません。Optional extension members are also not visible to C# or Visual Basic consumers. 他の F # コードでのみ使用できます。They can only be consumed in other F# code.

組み込みおよびオプションの型拡張の一般的な制限Generic limitation of intrinsic and optional type extensions

型変数が制限されているジェネリック型に対して型拡張を宣言できます。It's possible to declare a type extension on a generic type where the type variable is constrained. 要件は、拡張宣言の制約が宣言された型の制約と一致することです。The requirement is that the constraint of the extension declaration matches the constraint of the declared type.

ただし、宣言された型と型拡張の間で制約が一致した場合でも、宣言された型よりも型パラメーターに対して異なる要件を課す拡張メンバーの本体によって制約が推論される可能性があります。However, even when constraints are matched between a declared type and a type extension, it's possible for a constraint to be inferred by the body of an extended member that imposes a different requirement on the type parameter than the declared type. 次に例を示します。For example:

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

このコードを取得して、オプションの型拡張機能を使用することはできません。There is no way to get this code to work with an optional type extension:

  • そのように、 Sum メンバーは、型拡張機能で定義されているものとは異なる制約を 'T ( static member get_Zero および) 持ち static member (+) ます。As is, the Sum member has a different constraint on 'T (static member get_Zero and static member (+)) than what the type extension defines.
  • と同じ制約を持つように型拡張を変更すると、 Sum で定義されている制約とは一致しなくなり IEnumerable<'T> ます。Modifying the type extension to have the same constraint as Sum will no longer match the defined constraint on IEnumerable<'T>.
  • member this.Sumをに変更する member inline this.Sum と、型制約が一致しないというエラーが表示されます。Changing member this.Sum to member inline this.Sum will give an error that type constraints are mismatched.

必要なのは、"スペースで浮動小数点" を使用する静的メソッドであり、型を拡張するかのように表示できます。What is desired are static methods that "float in space" and can be presented as if they're extending a type. ここで拡張メソッドが必要になります。This is where extension methods become necessary.

拡張メソッドExtension methods

最後に、拡張メソッド ("C# スタイル拡張メンバー" とも呼ばれます) は、F # でクラスの静的メンバーメソッドとして宣言できます。Finally, extension methods (sometimes called "C# style extension members") can be declared in F# as a static member method on a class.

拡張メソッドは、型変数を制約するジェネリック型の拡張機能を定義する場合に便利です。Extension methods are useful for when you wish to define extensions on a generic type that will constrain the type variable. 次に例を示します。For example:

namespace Extensions

open System.Collections.Generic
open System.Runtime.CompilerServices

[<Extension>]
type IEnumerableExtensions =
    [<Extension>]
    static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs

このコードを使用すると、 SumIEnumerable<T> 開かれている Extensions かスコープ内にある限り、がに定義されているかのように表示されます。When used, this code will make it appear as if Sum is defined on IEnumerable<T>, so long as Extensions has been opened or is in scope.

VB.NET code で拡張機能を使用できるようにするに ExtensionAttribute は、アセンブリレベルで追加のが必要です。For the extension to be available to VB.NET code, an extra ExtensionAttribute is required at the assembly level:

module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()

その他の解説Other remarks

型拡張には、次の属性もあります。Type extensions also have the following attributes:

  • アクセス可能なすべての型を拡張できます。Any type that can be accessed can be extended.
  • 組み込みおよびオプションの型拡張では、メソッドだけでなく、 任意 のメンバー型を定義できます。Intrinsic and optional type extensions can define any member type, not just methods. たとえば、拡張機能のプロパティも可能です。So extension properties are also possible, for example.
  • self-identifier構文のトークンは、通常のメンバーと同じように、呼び出される型のインスタンスを表します。The self-identifier token in the syntax represents the instance of the type being invoked, just like ordinary members.
  • 拡張メンバーは、静的メンバーまたはインスタンスメンバーにすることができます。Extended members can be static or instance members.
  • 型の拡張機能の型変数は、宣言された型の制約と一致する必要があります。Type variables on a type extension must match the constraints of the declared type.

型拡張機能には、次の制限もあります。The following limitations also exist for type extensions:

  • 型拡張は、仮想メソッドまたは抽象メソッドをサポートしていません。Type extensions do not support virtual or abstract methods.
  • 型拡張は、拡張としてのオーバーライドメソッドをサポートしていません。Type extensions do not support override methods as augmentations.
  • 型拡張は、 静的に解決される型パラメーターをサポートしていません。Type extensions do not support Statically Resolved Type Parameters.
  • 省略可能な型拡張は、拡張としてのコンストラクターをサポートしていません。Optional Type extensions do not support constructors as augmentations.
  • 型の拡張子を型の 省略形に対して定義することはできません。Type extensions cannot be defined on type abbreviations.
  • 型拡張は、では有効ではありません byref<'T> (ただし、宣言することはできます)。Type extensions are not valid for byref<'T> (though they can be declared).
  • 型拡張は、属性に対しては有効ではありません (ただし、宣言することはできます)。Type extensions are not valid for attributes (though they can be declared).
  • 同じ名前の他のメソッドをオーバーロードする拡張機能を定義できますが、あいまいな呼び出しがある場合は、F # コンパイラによって非拡張メソッドが優先されます。You can define extensions that overload other methods of the same name, but the F# compiler gives preference to non-extension methods if there is an ambiguous call.

最後に、1つの型に対して複数の組み込み型拡張が存在する場合は、すべてのメンバーが一意である必要があります。Finally, if multiple intrinsic type extensions exist for one type, all members must be unique. オプション型拡張の場合は、1 つの型に対する複数の型拡張が存在する場合でも、各メンバーに同じ名前を付けることができます。For optional type extensions, members in different type extensions to the same type can have the same names. クライアント コードで同じメンバー名が定義されている 2 つの異なるスコープを開いている場合にのみ、あいまいさに対するエラーが発生します。Ambiguity errors occur only if client code opens two different scopes that define the same member names.

関連項目See also