为什么Array.zeroCreate仍然为非可空类型填充null?

时间:2012-12-16 12:51:30

标签: f#

这是否暗示每当我传递一个非可空类型的数组时,我仍然应该检查它是否为空?实际上甚至无法检查<> null但必须使用operator.unchecked。它比C#更好吗?

type test=
    {
        value: int
    }

let solution = Array.zeroCreate 10

solution.[0] <- {value = 1}
solution.[1].value   // System.NullReferenceException: Object reference not set to an instance of an object

type test =
  {value: int;}
val solution : test [] =
  [|{value = 1;}; null; null; null; null; null; null; null; null; null|]
val it : unit = ()

3 个答案:

答案 0 :(得分:3)

它取决于传递数组的位置。

如果数组是在F#中创建和使用的,那么不,你不需要检查null;实际上,你不应该检查null(使用Unchecked.defaultOf),因为在某些情况下,F#编译器优化了一些特殊值,如[](和None )通过在编译的IL中将它们表示为null

如果您正在使用另一种语言(例如C#)编写的代码来传入数组,那么是的,您仍然应该检查null。如果调用代码只是创建了数组并且没有进一步改变它,那么你只需要执行一次null检查。

编辑:以前是关于F#编译器如何使用null优化某些值的表示的讨论:Why is None represented as null?

答案 1 :(得分:2)

正如Array.zeroCreate的文档所示,它会将元素初始化为Unchecked.defaultof<_>。因此,这带有直接使用Unchecked.defaultof的所有相同注意事项。一般来说,我的建议是尽可能使用Array.create / Array.init,并将Array.zeroCreate视为可能的性能优化(在处理非可空类型时需要小心)。

答案 2 :(得分:1)

您正在创建一个记录类型,它实现为一个类,它确实可以为空。如果您打算创建一个结构,您的代码应如下所示:

type test =
    struct
        val value: int
        new(v) = { value = v }
        override x.ToString() = x.value.ToString()
    end

let solution = Array.zeroCreate 10

solution.[0] <- test(1)

输出:val solution : test [] = [|1; 0; 0; 0; 0; 0; 0; 0; 0; 0|]

您还可以使用Struct属性编写类型,从而为您节省缩进级别。

[<Struct>]
type test =
    val value: int
    new(v) = { value = v }
    override x.ToString() = x.value.ToString()