方法与函数在Go语言中的使用方式

huangapple go评论90阅读模式
英文:

Method vs Functions Usage golang

问题

我知道函数(func)和方法(method)之间的区别。但是我对以下用法感到困惑:

prod := Product{"title", "brand", "model"}
prod.Add()

或者:

prod := Product{"title", "brand", "model"}
products.Add(&prod) // products 是一个包(package)
英文:

I know differences between func and method. But I am confusing for usages between:

prod:=Product{"title","brand","model"}
prod.Add()

or:

prod:=Product{"title","brand","model"}
products.Add(&prod) // products is package

答案1

得分: 5

这是两种不同的情况,一种是属于Product实例的方法,另一种是属于products包的全局函数

type Product struct {
    Title string
    Brand string
    Model string
}

// 这个方法向Product的字段添加值
func (p *Product) Add(field, value string) {
    switch field {
    case "Title":
        p.Title = value
    case "Brand":
        p.Brand = value
    case "Model":
        p.Model = value
    }
}

上述代码提供了一种向Product实例添加值的方法,例如:

product1 := &Product{}
product1.Add("Title", "first_title")

第二种情况是从products包中公开的函数。在这种情况下,必须提供一个Product实例(或指针)作为参数。

package products

func Add(p *Product, field, value string) {
    // 与上述代码相同的switch-case
}

然后可以从任何其他包中使用Add函数。

package main

import (
    "path/to/products"
)

type Product struct {
    // ...
}

func main() {
    product1 := &Product{}
    products.Add(product1, "Title", "first_title")
}

通常在您的场景中,首选第一种方法,因为它将管理属性的功能封装在自身中。

第二种情况可能被视为“类方法方法”(对于那些来自像Python或Java这样的面向对象编程语言的人),其中包类似于类,公开的函数类似于类方法,它们更通用,可以在实现相同接口的许多类型中使用,例如:

package products

// 其中p是Product接口
func Add(p Product, field, value string) {
    // 与上述代码相同的switch-case
}

type Product interface {
    someMethod()
}

然后在另一个包中:

package main

import (
    "path/to/products"
)

type Car struct {
    Title string
    Brand string
    Model string
}

type Ship struct {
    // ...
}

type Airplane struct {
    // ...
}

// 所有类型都实现了`Product`接口,可以在`products.Add`中使用
func (c *Car) someMethod() {}
func (s *Ship) someMethod() {}
func (a *Airplane) someMethod() {}

func main() {
    plane := &Airplane{}
    products.Add(plane, "Model", "Boeing-747")
}
英文:

These are two distinct cases, one which is a method belongs to Product instance and one is a global function belongs to products package.

type Product struct {
        Title string
        Brand string
        Model string
}

// This method add value to a field in Product
func (p *Product) Add(field, value string) {
        switch field {
        case "Title":
                p.Title = value
        case "Brand":
                p.Brand = value
        case "Model":
                p.Model = value
        }
}

The above provide a method to add value to itself as an instance of Product, i.e.

product1 := &Product{}
product1.Add("Title", "first_title")

The second case is a public function exposed from a product package. In this case, an instance (or a pointer) of a Product must be supplied as an argument.

package products

func Add(p *Product, field, value string) {
        // Same switch-case as above
}

Add function then can be used from any other package.

package main

import (
        "path/to/products"
)

type Product struct {
        // ...
}

func main() {
        product1 := &Product{}
        products.Add(product1, "Title", "first_title")

Normally in your scenario, the first approach is preferred since it encapsulates the functionality of managing its attributes to itself.

The second scenario might be seen as a "class method approach" (for those coming from OOP like Python or Java) where the package is similar to class and the exposed functions similar to class methods which are more generic and can be used across many types which implement the same interface, like so:

package products

// where p is a Product interface
func Add(p Product, field, value string) {
        // Same switch-case as above
}

type Product interface {
        someMethod()
}

And from another package:

package main

import (
        "path/to/products"
)

type Car struct {
        Title string
        Brand string
        Model string
}

type Ship struct {
        // ...
}

type Airplane struct {
        // ...
}

// All types implement `Product` and can be used in `products.Add`
func (c *Car) someMethod() {}
func (s *Ship) someMethod() {}
func (a *Airplane) someMethod() {}

func main() {
       plane := &Airplane{}
       products.Add(plane, "Model", "Boeing-747")
}

答案2

得分: 2

根据规范,这是预期的行为:

方法的类型是将接收者作为第一个参数的函数的类型。

请参考 https://golang.org/ref/spec#Method_declarations

因此,当你在 Product 上声明 Add 方法时,你得到一个接受指向 Product 的指针作为其第一个参数的函数。所以你最终得到的是:

func (p *Product) Add() 

被翻译为

func Add(p *Product)

所以你的两个调用都是有效的,并且最终执行相同的操作。

英文:

This is expected as per the spec:

> The type of a method is the type of a function with the receiver as first argument.

See https://golang.org/ref/spec#Method_declarations

So when you declare the Add method on Product, you get a function that accepts a pointer to a Product as its first argument. So you end up with

func (p *Product) Add() 

being translated to

func Add(p *Product)

So both your calls are valid and end up doing the same

答案3

得分: 0

扩展了@Danilo的精彩答案:

package main

import "fmt"

type T struct {
    i int
}

func (t *T) F() {
    t = &T{1}
    fmt.Println(t.i)
}

func F(t *T) {
    fmt.Println(t.i)
}

func main() {
    t := T{2}
    (&t).F()
    F(&t)
}

方法func (t *T) F()的类型是函数func F(t *T)的类型,其中接收者(t *T)作为第一个参数。

英文:

Extending the fantastic answer by @Danilo:

package main

import "fmt"

type T struct {
	i int
}

func (t *T) F() {
	t = &T{1}
	fmt.Println(t.i)
}

func F(t *T) {
	fmt.Println(t.i)
}

func main() {
	t := T{2}
	(&t).F()
	F(&t)
}

The type of the method func (t *T) F() is the type of the function func F(t *T) with the receiver (t *T) as first argument.

huangapple
  • 本文由 发表于 2016年2月4日 04:51:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/35187450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定