F# 在F中重写ToString时避免堆栈溢出#
F#有一个非常强大的格式化指令“%a”,因为它触发格式化程序来展开类型并列出单个成员。在我们应用程序的某些地方,数据是使用ToString方法记录的(这有一些技术原因),然后对于像有区别的联合这样的类型,只记录一个类型名。太糟糕了,所以我们开始覆盖某些类型的ToString方法 举个例子:F# 在F中重写ToString时避免堆栈溢出#,f#,overriding,stack-overflow,tostring,F#,Overriding,Stack Overflow,Tostring,F#有一个非常强大的格式化指令“%a”,因为它触发格式化程序来展开类型并列出单个成员。在我们应用程序的某些地方,数据是使用ToString方法记录的(这有一些技术原因),然后对于像有区别的联合这样的类型,只记录一个类型名。太糟糕了,所以我们开始覆盖某些类型的ToString方法 举个例子: open System type DiscrUnion = | Text of string let t1 = DiscrUnion.Text "text" sprintf "%A" t1 spr
open System
type DiscrUnion =
| Text of string
let t1 = DiscrUnion.Text "text"
sprintf "%A" t1
sprintf "%s" <| t1.ToString()
type DiscrUnionWithToString =
| Text of string
override this.ToString() = sprintf "%A" this
let t2 = DiscrUnionWithToString.Text "text"
sprintf "%A" t2
sprintf "%s" <| t2.ToString()
type PocoType() =
member val Text : string = null with get, set
let t3 = PocoType()
t3.Text <- "text"
sprintf "%A" t3
sprintf "%s" <| t3.ToString()
type PocoTypeWithToString() =
member val Text : string = null with get, set
override this.ToString() = sprintf "%A" this
let t4 = PocoTypeWithToString()
t4.Text <- "text"
sprintf "%A" t4
sprintf "%s" <| t4.ToString()
开放系统
类型划分=
|字符串文本
设t1=DiscrUnion.Text“Text”
sprintf“%A”t1
sprintf“%s”简单的答案-不要在任何地方都对ToString
使用这种全面的实现
格式化字符串%A
启动了一个相当毛茸茸的基于反射的打印机,如果它没有以特殊方式处理,它可能会回到ToString
。请参阅anytoString或printf
的代码
一个更干净的解决方案是在记录对象时使用一个sprintf%A
,而不是让所有的DU实现一个样板ToString
,但是您说这不是一个选项
对于常规的.NET类(与特定于F#的记录或联合相反),不要使用this
——而是使用一些有意义的标识符或输出所有成员,或者执行任何您喜欢的操作。只是不要启动一个没完没了的到字符串的循环
发生StackOverflowException的原因是打印机使用格式化。如您所见,如果对象是一个F#对象,那么它在如何处理它们方面有特殊情况(元组、函数、联合、异常、记录)
但是,如果不是这些情况之一,它将使其成为ObjectValue(obj)
。稍后,在reprL
中,我们有一些特殊情况需要处理ObjectValue
s,例如字符串、数组、map/set、ienumerable,然后在最后,如果失败,它将使其成为Leaf
类型的基本布局(let basicL=LayoutOps.objL
)
很久以后,使用leafformatter
对Leaf
进行格式化leafformatter
可以处理原语,但当它处理复杂对象(如POCO)时,它会执行let text=obj.ToString()
,这会导致无限循环和StackOverflow异常
解决方案是不要在POCO上使用%A
好消息是,F#的下一个版本可能会有一个默认的ToString
记录/联合实现,该实现有效地覆盖了这个。ToString()=sprintf“%a”this
。它的实现已在此处部分完成:。它可能会解决你一开始遇到的问题
我得到了实际的属性:Text“Text”
当我面对那个问题时,我想到了这个
type DiscrUnionWithToString =
| Text of string
override text.ToString() =
match text with
| Text text -> text
谢谢你链接到anyToStringForPrintf-解释了很多。你的建议很简单,也很实用——我只是不希望通过一个电话就能轻松解决任何类型的问题。非常感谢!很高兴知道ToString可能会被改进,以便对F#原生类型更有用。