TL; DR: F#编译器在此上下文中将int
解释为int
operator,determined by Eugene Fotin和expanded 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#原始类型在大多数情况下都可与其他类型互换,但看起来我的心理模型中缺少某些东西。
答案 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
。