C#コードとF#を相互運用する必要があります。 nullは指定可能な値であるため、値がnullかどうかを確認する必要があります。ドキュメントでは、パターンマッチングの使用を推奨しています。
match value with
| null -> ...
| _ -> ...
私が抱えている問題は、元のコードがC#で次のように構造化されていることです。
if ( value != null ) {
...
}
F#で同等の操作を行うにはどうすればよいですか?パターンマッチングのノーオペレーションはありますか? ifステートメントでnullをチェックする方法はありますか?
Nullの場合に何もしたくない場合は、ユニット値()
を使用できます。
match value with
| null -> ()
| _ -> // your code here
もちろん、C#の場合と同じようにnullチェックを実行することもできます。この場合はおそらくより明確です。
if value <> null then
// your code here
何らかの理由で(まだ調査していませんが)not (obj.ReferenceEquals(value, null))
はvalue <> null
よりもパフォーマンスが優れています。 C#から使用する多くのF#コードを記述しているので、null
を扱いやすくするために "interop" module を残しています。また、パターンマッチングの際に最初に「通常の」ケースを使用したい場合は、アクティブパターンを使用できます。
let (|NotNull|_|) value =
if obj.ReferenceEquals(value, null) then None
else Some()
match value with
| NotNull ->
//do something with value
| _ -> nullArg "value"
単純なif
ステートメントが必要な場合は、これも機能します。
let inline notNull value = not (obj.ReferenceEquals(value, null))
if notNull value then
//do something with value
パフォーマンスの不一致に関するいくつかのベンチマークと追加情報を次に示します。
let inline isNull value = (value = null)
let inline isNullFast value = obj.ReferenceEquals(value, null)
let items = List.init 10000000 (fun _ -> null:obj)
let test f = items |> Seq.forall f |> printfn "%b"
#time "on"
test isNull //Real: 00:00:01.512, CPU: 00:00:01.513, GC gen0: 0, gen1: 0, gen2: 0
test isNullFast //Real: 00:00:00.195, CPU: 00:00:00.202, GC gen0: 0, gen1: 0, gen2: 0
775%のスピードアップ-悪くないです。 .NET Reflectorのコードを見てみると、ReferenceEquals
はネイティブ/アンマネージ関数です。 =
演算子は HashCompare.GenericEqualityIntrinsic<'T>
を呼び出し、最終的に内部関数 GenericEqualityObj
になります。 Reflectorでは、この美しさは122行のC#に逆コンパイルされます。明らかに、平等は複雑な問題です。 null
- checkingの場合、単純な参照比較で十分なので、微妙な等価セマンティクスのコストを回避できます。
パターンマッチングでは、等価演算子のオーバーヘッドも回避されます。次の関数はReferenceEquals
と同様に機能しますが、F#の外部で定義された型または[<AllowNullLiteral>]
で修飾された型でのみ機能します。
let inline isNullMatch value = match value with null -> true | _ -> false
test isNullMatch //Real: 00:00:00.205, CPU: 00:00:00.202, GC gen0: 0, gen1: 0, gen2: 0
Maslowのコメントに記載されているように、F#4.0では isNull
operator が追加されました。上記のisNullMatch
と同じように定義されているため、最適に実行されます。
C#または.NETライブラリ(F#ではなく)で一般的に宣言されている型がある場合、null
はその型の適切な値であり、値をnull
と簡単に比較できます。 kvbによる投稿。たとえば、C#の呼び出し元がRandom
のインスタンスを提供するとします。
let foo (arg:System.Random) =
if arg <> null then
// do something
C#の呼び出し元がF#で宣言された型を提供すると、事態はさらに複雑になります。 F#で宣言された型は値としてnull
を持たず、F#コンパイラーはそれらをnull
に割り当てたり、null
に対してチェックしたりできません。問題は、C#がこのチェックを行わず、C#の呼び出し元がnull
を返す可能性があることです。例えば:
type MyType(n:int) =
member x.Number = n
その場合、ボクシングまたはUnchecked.defaultOf<_>
が必要です。
let foo (m:MyType) =
if (box m) <> null then
// do something
let foo (m:MyType) =
if m <> Unchecked.defaultOf<_> then
// do something
もちろん、nullは一般にF#では推奨されませんが、...
例などに行かずに、いくつかの例を含む記事があります@ MSDN here =。 nullを検出する方法を具体的に明らかにします。そして、必要に応じて入力またはハンドルからそれを解析できます。
私は最近 similar dilemma に直面しました。私の問題は、C#コードから使用できるF#開発のAPIを公開したことです。 C#では、インターフェイスまたはクラスを受け入れるメソッドにnull
を渡しても問題はありませんが、メソッドとその引数の型がF#型の場合、nullチェックを適切に実行することはできません。
その理由は、F#型は最初はnull
リテラルを受け入れないためですが、技術的にはCLRの何もAPIが不適切に呼び出されることを防ぎます。特に、C#などのnullを許容する言語からは保護されません。 [<AllowNullLiteral>]
属性を使用しない限り、最初は自分自身を適切にnull保護できなくなりますが、これは非常に醜いアプローチです。
したがって、私は 関連トピックのこのソリューション に到達しました。これにより、F#コードをクリーンでF#フレンドリーな状態に保つことができます。一般的には、任意のオブジェクトを受け入れてOption
に変換する関数を作成し、null
の代わりにNone
に対して検証します。これは、F#Option.ofObj
関数で事前定義された関数を使用するのと似ていますが、後者では、渡されるオブジェクトを明示的にnullにできるようにする必要があります(したがって、醜いAllowNullLiteral
アノテーションが付けられます)。
私はこれを行う簡単な方法を見つけました
open System
Object.ReferenceEquals(value, null)