F# 空值测试无法检测空值



F# 中鲁棒测试 null 的正确方法是什么?

我有一个在Unity游戏引擎(闭源单声道c#/c ++引擎)之上构建的混合F#/C#项目。

我有一个 F# 函数,它调用可能返回 null 的 Unity API 函数。Unity 函数返回 null,但我的 F# 代码无法检测到这一点(我从测试数据的形状、附加调试器、插入日志语句等中都知道这一点)。我写的每个空测试似乎在应该为真时返回 false。第一次尝试:

let rec FindInParents<'t when 't : null> (go : GameObject) = 
    match go with 
    | null -> null
    | _ ->
        let comp = go.GetComponent<'t>() // GetComponent returns null
        match comp with
        | null -> FindInParents (go.transform.parent.gameObject) // This should be matched but isn't
        | _ -> comp // Always this branch

我也尝试了以下方法,但没有成功:

let rec FindInParents<'t when 't : null> (go : GameObject) = 
    if obj.ReferenceEquals (go, Unchecked.defaultof<'t>) then null 
    else 
        let comp = go.GetComponent<'t>() // Comp is null
        if obj.ReferenceEquals (comp, Unchecked.defaultof<'t>) then FindInParents<'t> (go.transform.parent.gameObject)
        else comp // Always this branch

我觉得我在这里错过了一些基本的东西,但到目前为止,它一直躲避着我。有什么指示吗?

编辑:我还应该指出,GetComponent总是返回UnityEngine.Component的子类型,并且始终是引用类型。UnityEngine.Component是UnityEngine.Object的一个子类型,它定义了一个自定义的==运算符(我认为这无关紧要,因为==不应该在第二个示例中调用(参见Daniel对[在F#中处理空值]的回答)

事实证明,Unity 对已在非托管端销毁但尚未在托管端收集的对象使用假 null 值。自定义== / !=运算符检查假空值。

对于像问题中的泛型函数,F# 将使用 IL 指令进行空测试 (brfalse.s) - 这显然不会检测到 Unity 假空值。显式测试 null 会导致调用 LanguagePrimitives.HashCompare.GenericEqualityIntrinsic 该调用也不知道 Unity 假 null。

解决方案是在 unity 对象上调用 Equals,以确保调用重载的 Unity 运算符:

let isUnityNull x = 
    let y = box x // In case of value types
    obj.ReferenceEquals (y, Unchecked.defaultof<_>) || // Regular null check
    y.Equals(Unchecked.defaultof<_>) // Will call Unity overload if needed

最新更新