型拡張 ( 拡張とも呼ばれます) は、以前に定義したオブジェクト型に新しいメンバーを追加できる一連の機能です。 次の 3 つの機能があります。
- 組み込み型拡張
- オプションの型拡張
- 拡張メソッド
それぞれ異なるシナリオで使用でき、トレードオフが異なります。
構文
// 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
}
Extensions
モジュールが作業中のスコープで開かれている限り、RepeatElements
IEnumerable<T>のメンバーであるかのようにアクセスできるようになりました。
リフレクションで調べると、拡張型にオプションの拡張は表示されません。 オプションの拡張機能はモジュール内に存在する必要があり、拡張機能を含むモジュールが開いているか、スコープ内にある場合にのみスコープ内に存在します。
オプションの拡張メンバーは、オブジェクト インスタンスが最初のパラメーターとして暗黙的に渡される静的メンバーにコンパイルされます。 ただし、宣言方法に従ってインスタンス メンバーまたは静的メンバーであるかのように動作します。
オプションの拡張メンバーは、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
このコードをオプションの型拡張で動作させる方法はありません。
- 同様に、
Sum
メンバーの'T
(static member get_Zero
とstatic member (+)
) に対する制約は、型拡張で定義されているものとは異なります。 -
Sum
と同じ制約を持つ型拡張を変更すると、IEnumerable<'T>
で定義された制約と一致しなくなります。 -
member this.Sum
をmember inline 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
このコードを使用すると、Extensions
が開かれているかスコープ内にある限り、IEnumerable<T>でSum
が定義されているかのように表示されます。
拡張機能を VB.NET コードで使用できるようにするには、アセンブリ レベルで追加の ExtensionAttribute
が必要です。
module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()
その他の備考
型拡張には、次の属性もあります。
- アクセスできる任意の型を拡張できます。
- 組み込み型拡張と省略可能な型拡張では、メソッドだけでなく、 任意 のメンバー型を定義できます。 そのため、たとえば拡張プロパティも可能です。
-
構文の
self-identifier
トークンは、通常のメンバーと同様に、呼び出される型のインスタンスを表します。 - 拡張メンバーには、静的メンバーまたはインスタンス メンバーを指定できます。
- 型拡張の型変数は、宣言された型の制約と一致する必要があります。
型拡張には、次の制限事項もあります。
- 型拡張では、仮想メソッドまたは抽象メソッドはサポートされていません。
- 型拡張では、拡張としてのオーバーライド メソッドはサポートされていません。
- 型拡張では、 静的に解決される型パラメーターはサポートされていません。
- オプションの型拡張では、拡張としてのコンストラクターはサポートされません。
- 型 の省略形に対して型拡張を定義することはできません。
- 型拡張は、
byref<'T>
では無効です (ただし、宣言できます)。 - 型拡張は属性に対して有効ではありません (ただし、宣言できます)。
- 同じ名前の他のメソッドをオーバーロードする拡張を定義できますが、あいまいな呼び出しがある場合、F# コンパイラは非拡張メソッドを優先します。
最後に、1 つの型に対して複数の組み込み型拡張が存在する場合は、すべてのメンバーが一意である必要があります。 省略可能な型拡張の場合、同じ型に対して異なる型拡張のメンバーは同じ名前を持つことができます。 あいまいさのエラーは、クライアント コードが同じメンバー名を定義する 2 つの異なるスコープを開いた場合にのみ発生します。
こちらも参照ください
.NET