F#には、変数やメンバーを型で定義するためのさまざまな方法があります。 let
、member val
およびmember this.
F#では、それらの違いは何ですか?静的メンバーと可変メンバーはどうですか?
@meziantouからの回答では、オプションの概要(およびオプションの動作の違い)がすでに示されているので、簡単な要約または推奨事項のリストを示します。
タイプ内でのみ表示されるローカル値(基本的にはlet
フィールドまたはprivate
関数)を定義する場合は、private
または_let mutable
_を使用します。トップレベルのモジュール内では、これらはパブリックにアクセス可能で、一度評価されます。モジュールレベルの_let mutable
_は、バッキング値のない単一の書き込み可能なフィールドを作成します。
val
を使用して自動プロパティを作成できます。これは_member val Foo = .. with get
_の略です。 F#から、これはフィールドと見なされますが、変更を防ぐためにバッキングフィールドを持つgetプロパティとして内部的に実装されています。
_val mutable
_を使用してパブリックフィールドを定義できますが、実際にパブリックフィールドが必要でない限り、これはお勧めしません(たとえば、一部の.NETライブラリでは、この構造の型が必要になる場合があります)。
_member x.Foo = ...
_を使用することは、型から状態を(読み取り専用で)公開するための最良の方法です。ほとんどのF#型は不変であるため、おそらくこれが最も一般的なパブリックメンバーです。取得専用のインスタンスプロパティの略です。
member x.Foo with get() = .. and set(value) ...
の使用は、ゲッターとセッターで独自のカスタムコードを使用してget/setプロパティを作成する必要がある場合に役立ちます。これは、変更可能なオブジェクトを作成するときに便利な場合があります。
_member val Foo = ... with get, set
_の使用は、基本的にはC#で自動実装されるプロパティと同じです。これは、可変のバッキングフィールドを読み書きするゲッターとセッターを持つ可変のプロパティが必要な場合に便利です。
型で_static let
_を使用すると、静的(クラスレベル)の読み取り専用フィールドが作成されます。これは、バッキングフィールドを持つプロパティを内部的に作成します。バッキングフィールドのない読み取り/書き込み静的フィールドには、_static mutable let ...
_を使用します。
_static val mutable private
_を使用すると、バッキングフィールドを持つ静的な読み取り/書き込み自動プロパティが作成され、パブリックにすることはできません。
何が起こっているのかを逆コンパイルする方が簡単であることがわかりました。
type Region() =
let mutable t = 0.0f
member val Width = 0.0f
member x.Height = 0.0f
member val Left = 0.0f with get,set
member x.Top with get() = 0.0f and set(value) = t <- value
実際には次のとおりです。
public class Region
{
internal float t;
internal float Width@;
internal float Left@;
public float Width
{
get
{
return this.Width@;
}
}
public float Height
{
get
{
return 0f;
}
}
public float Left
{
get
{
return this.Left@;
}
set
{
this.Left@ = value;
}
}
public float Top
{
get
{
return 0f;
}
set
{
this.t = value;
}
}
public Region() : this()
{
this.t = 0f;
this.Width@ = 0f;
this.Left@ = 0f;
}
}
このサンプルでは、構文の違いについて説明します。
type MyClass() =
let random = new System.Random()
[<DefaultValue>] val mutable field : int
member val AutoProperty = random.Next() with get, set
member this.ExplicitProperty = random.Next()
let c = new MyClass()
// c.random is not accessible
c.field <- 42 // 'field' is accessible
// An automatic property is only evaluated upon initialization, and not every time the property is accessed
printfn "AutoProperty = %d" c.AutoProperty // x
printfn "AutoProperty = %d" c.AutoProperty // Still x
// The value of the explicit property is evaluated each time
printfn "ExplicitProperty = %d" c.ExplicitProperty // y
printfn "ExplicitProperty = %d" c.ExplicitProperty // The value is re-evaluated so you'll get a different value