1

下面的代码产生了不受欢迎的

[20010101 20010102]。

当取消注释 String 函数时,它会产生更好的效果(但不是我的实现):

[{20010101 1.5} {20010102 2.5}]

但是,永远不会调用 String func。我看到 DateValue 中的 Date 是匿名的,因此func (Date) StringDateValue 正在使用它。

所以我的问题是:

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)
}
4

2 回答 2

1

这是因为您传入了一个 DateValues 切片,而不是 DateValue 指针。由于您已经为 定义了 String 方法*DataValue*DateValue因此实现了 Stringer 接口。这也阻止了 DateValue 通过其匿名 Date 成员实现 Stringer 接口,因为只有值类型 ( DateValue) 或指针类型 ( *DateValue) 之一可用于实现接口。因此,当 fmt.Println 打印切片的内容时,它会看到元素不是 Stringers,并使用默认的结构格式而不是您定义的方法,给出[{20010101 1.5} {20010102 2.5}].

您可以将 OrderedValues 设为 a[]*DateValue或定义func (dv DateValue) String() string,而不是指针版本。

于 2012-06-14T19:22:14.407 回答
0

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.

于 2012-06-18T02:34:12.317 回答