英文:
Adding a func never called improves behavior?
问题
以下是翻译好的部分:
代码部分不要翻译,只返回翻译好的部分,不要有别的内容,不要回答我要翻译的问题。
下面的代码产生了不理想的输出:
> [20010101 20010102].
当取消注释String函数时,它会产生更好的输出(但不是我自己的实现):
> [{20010101 1.5} {20010102 2.5}]
然而,String函数从未被调用过。
我看到DateValue中的Date是匿名的,因此func (Date) String
被DateValue使用。
所以我的问题是:
1)这是语言问题,fmt.Println
的实现问题,还是其他问题?注意:如果我从:
func (*DateValue) String() string
切换到
func (DateValue) String() string
我的函数至少会被调用并引发恐慌。所以如果我真的想要调用我的方法,我可以这样做,但是假设DateValue是一个非常大的对象,我只想通过引用传递它。
2)在使用反射的Stringer和json编码等功能时,如何有效地混合匿名字段和功能?例如,为作为匿名字段使用的类型添加String或MarshalJSON方法可能会导致奇怪的行为(例如,只打印或编码整个部分)。
package main
import (
"fmt"
"time"
)
type Date int64
func (d Date) String() string {
t := time.Unix(int64(d),0).UTC()
return fmt.Sprintf("%04d%02d%02d", t.Year(), int(t.Month()), t.Day())
}
type DateValue struct {
Date
Value float64
}
type OrderedValues []DateValue
/*
// ADD THIS BACK and note that this is never called but both pieces of
// DateValue are printed, whereas, without this only the date is printed
func (dv *DateValue) String() string {
panic("Oops")
return fmt.Sprintf("DV(%s,%f)", dv.Date, dv.Value )
}
*/
func main() {
d1, d2 := Date(978307200),Date(978307200+24*60*60)
ov1 := OrderedValues{{ d1, 1.5 }, { d2, 2.5 }}
fmt.Println(ov1)
}
英文:
The code below produces undesirable
> [20010101 20010102].
When uncommenting the String func it produces better (but not my implementation):
> [{20010101 1.5} {20010102 2.5}]
However that String func is never called.
I see that Date in DateValue is anonymous and therefore func (Date) String
is being used by DateValue.
So my questions are:
-
Is this a language issue, a
fmt.Println
implementation issue, or
something else? Note: if I switch from:func (*DateValue) String() string
to
func (DateValue) String() string
my function is at least called and panic ensues. So if I really want my method called I could do that, but assume DateValue is really a very large object which I only want to pass by reference.
-
What is a good strategy for mixing anonymous fields with
functionality like Stringer and json encoding that use reflection
under the covers? For example adding a String or MarshalJSON method
for a type that happens to be used as an anonymous field can cause
strange behavior (like you only print or encode part of the whole).package main
import (
"fmt"
"time"
)type Date int64
func (d Date) String() string {
t := time.Unix(int64(d),0).UTC()
return fmt.Sprintf("%04d%02d%02d", t.Year(), int(t.Month()), t.Day())
}type DateValue struct {
Date
Value float64
}type OrderedValues []DateValue
/*
// ADD THIS BACK and note that this is never called but both pieces of
// DateValue are printed, whereas, without this only the date is printed
func (dv *DateValue) String() string {
panic("Oops")
return fmt.Sprintf("DV(%s,%f)", dv.Date, dv.Value )
}
*/func main() {
d1, d2 := Date(978307200),Date(978307200+246060)
ov1 := OrderedValues{{ d1, 1.5 }, { d2, 2.5 }}
fmt.Println(ov1)
}
答案1
得分: 1
这是因为你传递了一个 DateValues 的切片而不是 DateValue 的指针。由于你为 *DataValue
定义了 String 方法,所以 *DateValue
是满足 Stringer 接口的类型。这也阻止了 DateValue 通过其匿名的 Date 成员来满足 Stringer 接口,因为只能使用值类型(DateValue)或指针类型(*DateValue)中的一个来满足接口。因此,当 fmt.Println 打印切片的内容时,它发现元素不是 Stringers,于是使用默认的结构格式化而不是你定义的方法,得到 [{20010101 1.5} {20010102 2.5}]
。
你可以将 OrderedValues 改为 []*DateValue
,或者定义 func (dv DateValue) String() string
而不是指针版本。
英文:
It's because you've passed in a slice of DateValues and not DateValue pointers. Since you've defined the String method for *DataValue
, *DateValue
is what fulfills the Stringer interface. This also prevents DateValue from fulfilling the Stringer interface via its anonymous Date member, because only one of either the value type (DateValue
) or the pointer type (*DateValue
) can be used to fulfill an interface. So, when fmt.Println is printing the contents of the slice, it sees that the elements are not Stringers, and uses the default struct formatting instead of the method you defined, giving [{20010101 1.5} {20010102 2.5}]
.
You can either make OrderedValues a []*DateValue
or define func (dv DateValue) String() string
instead of the pointer version.
答案2
得分: 0
根据@SteveM的说法,我将其简化为一个更简单的测试案例:
package main
import "fmt"
type Fooable interface {
Foo()
}
type A int
func (a A) Foo() { }
type B struct {
A
}
// 取消下面的方法注释,它将打印出false
//func (b *B) Foo() { }
func main() {
var x interface{} = B{}
_, ok := x.(Fooable)
fmt.Println(ok) // 打印true
}
换句话说,当定义*B
的Foo
方法时,Foo
方法不是B
的方法集的一部分。
从规范中的阅读来看,我没有看到清晰的解释发生了什么。最接近的部分似乎在选择器的章节中:
> 对于类型为T或*T的值x,其中T不是接口类型,x.f表示T中最浅深度的具有f的字段或方法。
我唯一能想到的解释是,当它在B
中寻找最浅深度的Foo
方法时,它也会考虑*B
的方法,出于某种原因(即使我们考虑的是类型B
而不是*B
);而*B
中的Foo
确实比A
中的Foo
更浅,所以它将*B
中的Foo
作为候选;然后它发现*B
中的Foo
不起作用,因为它在*B
中而不是B
中,所以它完全删除了Foo
(即使从A
继承了一个有效的Foo
)。
如果这确实是发生的情况,那么我同意原帖中的观点,即添加一个方法到*B
会导致删除B
中的一个方法,这是非常令人费解的。
也许对Go更熟悉的人可以澄清这一点。
英文:
Based on what @SteveM said, I distilled it to a simpler test case:
package main
import "fmt"
type Fooable interface {
Foo()
}
type A int
func (a A) Foo() { }
type B struct {
A
}
// Uncomment the following method and it will print false
//func (b *B) Foo() { }
func main() {
var x interface{} = B{}
_, ok := x.(Fooable)
fmt.Println(ok) // prints true
}
In other words, the Foo
method is not part of the method set of B
when the Foo
method for *B
is defined.
From reading the spec, I don't see a clear explanation of what is happening. The closest part seems to be in the section on selectors:
> For a value x of type T or *T where T is not an interface type, x.f
> denotes the field or method at the shallowest depth in T where there
> is such an f.
The only way I can see this explaining what is going on is if when it is looking for a method Foo
at shallowest depth in B
, it takes into consideration the methods for *B
too, for some reason (even though we are considering type B
not *B
); and the Foo
in *B
is indeed shallower than the Foo
in A
, so it takes that one as the candidate; and then it sees that that Foo
doesn't work, since it's in *B
and not B
, so it gets rid of Foo
altogether (even though there is a valid one inherited from A
).
If this is indeed what is going on, then I agree with the OP in that this is very counter-intuitive that adding a method to *B
would have the reverse consequence of removing a method from B
.
Maybe someone more familiar with Go can clarify this.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论