F#int.MaxValue是“不是有效的常量表达式”,但System.Int32.MaxValue是?

时间:2013-11-28 16:47:44

标签: .net f# data-annotations

TL; DR: F#编译器在此上下文中将int解释为int operatordetermined by Eugene Fotinexpanded upon by Gene Belitski。最佳解决方法是使用System.Int32.MaxValue或唯一类型别名,如下所述。


考虑以下记录类型:

type User = {
    Username : string
}

我希望Username长度至少为三个字符,因此我使用StringLength属性。没有最大长度,因此我将其设置为int.MaxValue

type User = {
    [<StringLength(int.MaxValue, MinimumLength=3)>]
    Username : string
}

这给了我以下错误:

  

这不是有效的常量表达式或自定义属性值。

如果我使用System.Int32代替,那么一切都很好看:

type User = {
    [<StringLength(System.Int32.MaxValue, MinimumLength=3)>]
    Username : string
}

如果别名int

,它也会编译
type User = {
    [<StringLength(num.MaxValue, MinimumLength=3)>]
    Username : string
}
and num = int

或完全符合类型:

type User = {
    [<StringLength(Microsoft.FSharp.Core.int.MaxValue, MinimumLength=3)>]
    Username : string
}

我检查了F#来源和int is defined exactly as you would expect

type int32 = System.Int32
// Then, a few lines later…
type int = int32

发生了什么事?我假设F#原始类型在大多数情况下都可与其他类型互换,但看起来我的心理模型中缺少某些东西。

2 个答案:

答案 0 :(得分:11)

这就是F#类型推理如何在不同的语境中使用不同的语法实体同时具有相同的名称,在int的情况下可能是以下任何一种:

  • 全名int:'T->int
  • 的函数Microsoft.FSharp.Core.Operators.int
  • type int = int32全名Microsoft.FSharp.Core.int
  • type int<'Measure> = int全名Microsoft.FSharp.Core.int<_>

演示此工作方式的一种方法是以下方案:如果我们只输入

int;;
在FSI中,我们会得到像

这样的东西
val it : (int -> int) = <fun:it@3>

换句话说,它是一个不能与MaxValue属性相关联的函数:

> int.MaxValue;;

int.MaxValue;;
----^^^^^^^^

... error FS0039: The field, constructor or member 'MaxValue' is not defined

同样适用于int32,当在表达式的上下文中使用时,它被FSI推断为另一个具有签名(int -> int32)的函数。

现在谈到

type num = int

在此上下文中int被推断为System.Int32的类型名称缩写,因此num也是类型缩写,但现在名称歧义没有位置,因此{ {1}}正好推断出我们的期望,在FSI中给出

num.MaxValue

最后,当你使用> num.MaxValue;; val it : int = 2147483647 明确引用类型实体时,没有模糊性的地方,所以它按预期工作。

回到你的带有属性参数的用例 - 在这种情况下,Microsoft.FSharp.Core.int被类型推断视为表达式的一部分,以传递参数值,即作为函数,除非你明确或间接地设置另一种解释。

答案 1 :(得分:5)

看起来编译器将属性参数中的int视为转换函数int