29

サンプルプログラムの場合:

type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving

let classFactory () = MyClass("up to you")
let live () =
    let instance = classFactory()
    if instance = null then raise(System.Exception("null is not living... that's why OO languages die from bugs"))
    instance

このクラスを暗黙的に型指定された関数の戻り値として使用し、 nullと比較すると「タイプ'MyClass'には適切な値としてnullがありません」というエラーが発生します(C#依存性注入との互換性要件のb / c F#オプションタイプに頼ることはできません)。

nullチェックを次のように変更することで、これを簡単に修正できます。

if instance :> obj = null then

しかし、私はこれが完全に「間違っている」ことを知っています(「感じる」)。特に、MyClassが(C#の背景から言えば)ボックス化する必要のない参照型であると考えると。

「F#値の制限」とそれが型推論にどのように影響するかについて読んだことがありますが、それがこのシナリオにどのように適用されるかを光らせることはできません。

Q:これを行う別の方法はありますか?

余談ですが#1:エラーを取得するためのより簡単な方法を見つけました...

type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving
let nullMyClass : MyClass = null

余談ですが、#2:考えずにSystem.Nullableを試しました... MyClassは参照型であり、Nullable <_>が必要とする値型(構造体)ではありません。だから、私は本当に参照型を扱っていることを私に安心させ、なぜオブジェクトキャストが突然これを機能させるのか疑問に思います。

更新:興味のある人のために、私はこれをCommonServiceLocatorの1つのソリューションとして以下の3つの機能で使用しました。要求される各サービスはnull[<AllowNullLiteral>]をサポートする必要があるため、サービスクラスがF#で定義されている場合は、 :を追加する必要があります。

let private getServiceLocator () =
    try Some(Microsoft.Practices.ServiceLocation.ServiceLocator.Current)
    with | _ -> None

let private getService serviceFactory =
    let serviceLocator = getServiceLocator()
    let service = match serviceLocator with 
                  | None -> serviceFactory()
                  | _ -> 
                    match serviceLocator.Value.GetInstance<'a>() with
                    | null -> serviceFactory()
                    | svc -> svc
    match service with
    | null -> None
    | _ -> Some(service)

let private getRequiredService serviceFactory =
    let service = getService serviceFactory
    match service with
    | None -> raise(MissingServiceException(""))
    | _ -> service.Value
4

2 に答える 2

49

[<AllowNullLiteral>]次の属性を使用します。

[<AllowNullLiteral>]
type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving

デフォルトでは、F#タイプはnullを許可しません(天に感謝します!)。この属性は、他の.NET言語との相互運用に役立ち、nullとの割り当て/比較を可能にします。

于 2012-07-27T22:32:07.830 に答える
21

The problem with the AllowNullLiteral attribute is that in addition to allowing you to compare your objects to null, it also makes it possible to set your objects to null.

Assuming that this is not desirable for your use-case, there is an easy alternative with unobservable performance impact:

let inline isNull (x:^T when ^T : not struct) = obj.ReferenceEquals (x, null)

Then rather than doing if instance = null then, do if isNull instance then instead.

This will work for any reference type (including records and DUs), but does not introduce the possibility of setting objects of your F# types to null from F# – the best of both worlds.

于 2012-07-27T23:39:58.083 に答える