【问题标题】:Method Sets (Pointer vs Value Receiver)方法集(指针与值接收器)
【发布时间】:2015-11-07 20:09:18
【问题描述】:

我很难理解为什么这些规则与指针类型 .vs 的方法集相关联。值类型

谁能解释一下原因(从接口表的角度)

(来自威廉肯尼迪博客的片段)

Values          Methods Receivers
-----------------------------------------------
T               (t T)
*T              (t T) and (t *T)

Methods Receivers    Values
-----------------------------------------------
(t T)                 T and *T
(t *T)                *T

规范中的片段

方法集

一个类型可能有一个与之关联的方法集。接口类型的方法集就是它的接口。 任何其他类型 T 的方法集由所有以接收者类型 T 声明的方法组成。对应指针类型 *T 的方法集是所有以接收者 *T 或 T 声明的方法的集合(即它还包含该方法T 组)。进一步的规则适用于包含匿名字段的结构,如结构类型部分所述。任何其他类型都有一个空方法集。在方法集中,每个方法都必须有一个唯一的非空方法名。

类型的方法集决定了该类型实现的接口以及可以使用该类型的接收器调用的方法。

【问题讨论】:

标签: go


【解决方案1】:
  1. 如果您有*T,您可以调用接收器类型为*T 的方法以及接收器类型为T 的方法(您引用的段落,Method Sets)。
  2. 如果您有一个T 并且它是addressable,您可以调用接收器类型为*T 的方法以及接收器类型为T 的方法,因为该方法调用t.Meth()将等同于(&t).Meth() (Calls)。
  3. 如果你有一个 T 并且它是不可寻址的(例如,函数调用的结果,或者索引到映射的结果),Go 无法获得指向它的指针,所以你可以只调用接收器类型为T 的方法,而不是*T
  4. 如果你有一个接口I,并且I的方法集中的部分或全部方法是由接收者为*T的方法提供的(其余的由接收者为的方法提供) T),然后*T 满足接口I,但T 不满足。那是因为*T 的方法集包含T 的方法,但反之则不然(再次回到第一点)。

简而言之,您可以将方法与值接收器和方法与指针接收器混合和匹配,并将它们与包含值和指针的变量一起使用,而不必担心哪个是哪个。两者都可以工作,语法是一样的。但是,如果需要带有指针接收器的方法来满足接口,那么只有一个指针可以分配给该接口——一个值将是无效的。

【讨论】:

  • 感谢您清晰的解释。希望文档能够清晰地表达出来
  • 我同意@user2780187。我有一个问题@hobbs,关于您回答的第 3 点。 T 的示例可能是不可“寻址”的,因此我们不能使用指针接收器 *T 调用它的方法。从您的参考链接到另一个stackoverflow 问题,我找到了一个示例,即返回值。但想知道是否还有其他示例。
  • @srini calculateArea(42) 不可寻址
  • @srini 大多数匿名值。这里有一个很好的描述:utcc.utoronto.ca/~cks/space/blog/programming/…
【解决方案2】:

来自Golang FAQ

正如 Go 规范所说,一个类型 T 的方法集由所有接收者类型为 T 的方法组成,而对应指针类型 *T 的方法集由所有接收者为 *T 或 T 的方法组成。这意味着方法集*T 的包括 T 的,但不包括相反的。

产生这种区别是因为如果接口值包含指针 *T,则方法调用可以通过解引用指针来获取值,但如果接口值包含值 T,则方法调用没有安全的获取方式一个指针。 (这样做将允许方法修改接口内的值的内容,这是语言规范所不允许的。)

即使在编译器可以将值的地址传递给方法的情况下,如果方法修改了值,则更改将在调用者中丢失。例如,如果 bytes.Buffer 的 Write 方法使用值接收器而不是指针,则此代码:

var buf bytes.Buffer
io.Copy(buf, os.Stdin)

会将标准输入复制到 buf 的副本中,而不是 buf 本身。这几乎不是我们想要的行为。

关于引擎盖下的 Golang 界面。

【讨论】:

  • 我觉得这个参考很好,因为它给出了比标准的方法集更深层次的解释。关于为什么以这种方式设计方法集的真正问题的关键是“这样做将允许方法修改接口内的值的内容,这是语言规范所不允许的”。但是我在语言规范中找不到这个特殊的禁令。可以点一个吗?
【解决方案3】:

-当我们有一个类型时,我们可以在其上附加方法,这些附加到类型的方法称为它的方法集。

  • 根据 Pointer 或非指针值,它将确定附加到它的方法。

案例:1 接收者 (t T) 值 T => https://go.dev/play/p/_agcEVFaySx

type square struct { 
    length int
}

type shape interface { shape as an interface
    area() int
}
// receiver(t T)
func (sq square) area() int { 
    return sq.length * sq.length
}

func describe(s shape) {
    fmt.Println("area", s.area())
}

func main() {
    sq := square{
        length: 5,
    }
    describe(sq)// value `sq` (T)
}

案例 2: 接收方 (t T) 值 T

// receiver(t *T)
func (sq *square) area() int { 
    return sq.length * sq.length
}

func main() {
    describe(sq)// value sq (T)
}

案例 4: 接收方 (t *T) 值 T

// receiver(t *T)
func (sq *square) area() int { 
    return sq.length * sq.length
}

func main() {
    describe(&sq)// value sq (*T)
}

案例 4: 接收方 (t *T) 值 T 此案失败

// receiver(t *T)
func (sq *square) area() int { 
    return sq.length * sq.length
}

func main() {
    describe(&sq)// value sq (T)
}

我们输入的是普通值而不是指针,但是方法接收者接受指针值,它不会接受,失败。

但是我们像sq.area()这样调用area方法//而不是使用接口来访问它。

【讨论】:

    猜你喜欢
    • 2015-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多