F# null 测试无法检测到空值
F# null test fails to detect null values
在 F# 中可靠地测试 null 的正确方法是什么?
我有一个构建在 Unity 游戏引擎(这是一个闭源单声道 C#/C++ 引擎)之上的 F#/C# 混合项目。
我有一个调用 Unity API 函数的 F# 函数,该函数可能 return 为空。 Unity 函数 return 为空,但我的 F# 代码无法检测到此 (我从测试数据的形状、附加调试器、插入日志语句等方面都知道这一点) .我写的每一个 null 测试似乎 returns false 而它应该是 true。第一次尝试:
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 总是 return 是 UnityEngine.Component 的子类型,并且始终是引用类型。 UnityEngine.Component 是 UnityEngine.Object 的子类型,它定义了一个自定义 == 运算符(我认为这不相关,因为在第二个示例中不应调用 == (请参阅 Daniel 对 [Handling Null Values in F#)
事实证明,Unity 对已在非托管端销毁但尚未在托管端收集的对象使用伪造的空值。自定义 == / !=
运算符检查假空值。
对于问题中的通用函数,F# 将使用 IL 指令进行空值测试 (brfalse.s
) - 这显然不会检测到 Unity 假空值。对 null 的测试显式导致对 LanguagePrimitives.HashCompare.GenericEqualityIntrinsic
的调用,它也不知道 Unity 伪空值。
解决方法是在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
在 F# 中可靠地测试 null 的正确方法是什么?
我有一个构建在 Unity 游戏引擎(这是一个闭源单声道 C#/C++ 引擎)之上的 F#/C# 混合项目。
我有一个调用 Unity API 函数的 F# 函数,该函数可能 return 为空。 Unity 函数 return 为空,但我的 F# 代码无法检测到此 (我从测试数据的形状、附加调试器、插入日志语句等方面都知道这一点) .我写的每一个 null 测试似乎 returns false 而它应该是 true。第一次尝试:
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 总是 return 是 UnityEngine.Component 的子类型,并且始终是引用类型。 UnityEngine.Component 是 UnityEngine.Object 的子类型,它定义了一个自定义 == 运算符(我认为这不相关,因为在第二个示例中不应调用 == (请参阅 Daniel 对 [Handling Null Values in F#)
事实证明,Unity 对已在非托管端销毁但尚未在托管端收集的对象使用伪造的空值。自定义 == / !=
运算符检查假空值。
对于问题中的通用函数,F# 将使用 IL 指令进行空值测试 (brfalse.s
) - 这显然不会检测到 Unity 假空值。对 null 的测试显式导致对 LanguagePrimitives.HashCompare.GenericEqualityIntrinsic
的调用,它也不知道 Unity 伪空值。
解决方法是在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