Go——方法调用

1、一般调用

类型方法的一般调用方式:

TypeInstanceName.MethodName(ParamList)
  • TypeInstanceName:类型实例名或指向实例的指针变量名
  • MethodName:类型方法名
  • ParamList:方法实参
type T struct {
	a int
}

func (t T) Get() int {
	return t.a
}

func (t *T) Set(i int) {
	t.a = i
}

var t = &T{}

//普通方法调用
t.Set(2)

//普通方法调用
t.Get()

2、方法值

变量x的静态类型是T,M是类型T的一个方法,x.T被称为方法值(method value)。x.T是一个函数类型变量,可以赋值给其他变量,并像普通的函数名一样使用。例如:

f := x.M
f (args...)
//等价于
x.M(args ...)

方法值(method value)其实是一个带有闭包的函数变量,其底层实现原理和带有闭包的匿名函数类似,接收值被隐式地绑定到方法值(method value)的闭包环境中。后续调用不需要再显式地传递接收者。例如:

package main

import "fmt"

func main() {
	var t = &T{}
	//method value
	f := t.Set

	//方法值调用
	f(2)
	t.Print() //结果为0xc42001c070, &{2}, 2

	//方法值调用
	f(3)
	t.Print() //结果为0xc42001c070, &{3}, 3
}

type T struct {
	a int
}

func (t T) Get() int {
	return t.a
}

func (t *T) Set(i int) {
	t.a = i
}

func (t *T) Print() {
	fmt.Printf("%p, %v, %d \n", t, t, t.a)
}

3、方法表达式(method expression)

方法表达式相当于提供一种语法将类型方法调用显式地转换为函数调用,接收者(receiver)必须显式地传递进去。下面定义一个类型T,增加两个方法,方法Gt的接收者为T,方法Set的接收者类型为*T。

type T struct {
	a int
}

func (t T) Get() int {
	return t.a
}

func (t *T) Set(i int) {
	t.a = i
}

func (t *T) Print() {
	fmt.Printf("%p, %v, %d \n", t, t, t.a)
}

表达式T.Get(*T).Set被称为方法表达式(method expression),方法表达式可以看作函数名,只不过这个函数的首个参数是接收者的实例或指针。T.Get的函数签名是func(t T) int,(*T).Set的函数签名是func(t*T, int)。注意:这里的T.Get不能写成(*T).Get,(*T).Set也不能写成T.Set,在方法表达式中编译器不会做自动转换。例如:

//如下方法表达式调用都是等价的
t := T{a: 1}

//普通方法调用
t.Get()

//方法表达式调用
(T).Get(t)

//方法表达式调用
f1 := T.Get; f1(t)

//方法表达式调用
f2 := (T).Get; f2(t)

//如下方法表达式调用都是等价的
(*T).Set(&t, 1)
f3 := (*T).Set; f3(&t, 1)

4、方法集(method set)

命名类型方法接收者有两种类型,一个是值类型,另一个是指针类型,这个和函数是一样的,前者的形参是值类型,后者的形参是指针类型。无论接收者是什么类型,方法和函数的实参传递都是值拷贝。如果接收者是值类型,则传递的是值的副本;如果接收者是指针类型,则传递的是指针的副本。

package main

import "fmt"

func main() {
	var a Int = 10
	var b Int = 20

	c := a.Max(b)
	c.Print()    //value=50
	(&c).Print() //value=50,内部被编译器转换为c.Print()

	a.Set(20) //内部被编译器转化为(&a).Set(20)
	a.Print() //value=20

	(&a).Set(30)
	a.Print() //value=30
}

type Int int

func (a Int) Max(b Int) Int {
	if a >= b {
		return a
	} else {
		return b
	}
}

func (i *Int) Set(a Int) {
	*i = a
}

func (i Int) Print() {
	fmt.Printf("value=%d\n", i)
}

上面示例定义了一个新类型Int,新类型的底层类型是int,Int虽然不能继承int的方法,但底层类型支持的操作(算术运算和赋值运算)可以被上层类型继承,这是Go类型系统的一个特点。

接收者是Int类型的方法集合(method set):

func (i Int)Print()
func (a Int)Max(b Int)Int

接收者是*Int类型的方法集合(method set):

func (i *Int)Set(a Int)

为了简化描述,将接收者(receiver)为值类型T的方法的集合记录为S,将接收者(receiver)为指针类型T的方法的集合统称为S。类型的方法集总结如下:

  1. T类型的方法集是S。
  2. T类型的方法集是S和S。
    从上面的示例可以看出,在直接使用类型实例调用类型的方法时,无论值类型变量还是指针类型变量,都可以调用类型的所有方法,原因是编译器在编译期间能够识别出这种调用关系,做了自动的转换。比如a.Set()使用值类型实例调用指针接收者方法,编译器会自动将其转换为(&a).Set(),(&a),Print()使用指针类型实例调用值类型接收者方法,编译器自动将其转化为a.Print()。
5、值调用和表达式调用的方法集

具体类型实例变量直接调用其方法时,编译器会所调用方法进行自动转换,即使接收者是指针的方法,仍然可以使用值类型变量进行调用。

下面讨论在以下两种情况下编译器是否会进行方法的自动转换。

  1. 通过类型字面量显式地进行值调用和表达式调用,可以看到在这种情况下编译器不会做自动转换,会进行严格的方法集检查。例如:
type Data struct{}

func (Data)Testvalue(){}

func (*Data)TestPointer(){}

//这种字面量显式调用,无论值调用,还是表达式调用,
//编译器都不会进行方法集的自动转换,编译器会严格校验方法集
//*Data方法集是TestPointer和TestValue
//Data方法集只有TestValue

(*Data)(&struct{}{}).TestPointer()//显式的调用
(*Data)(&struct{}{}).TestValue()//显式的调用

(Data)(struct{}(}).Testvalue()//method value
Data.Testvalue(struct{}{})//method expression


//如下调用因为方法集和不匹配而失败
//Data.TestPoiter(struct(]{}) //type Data has no method TestPoiter
//(Data)(struct(){}).TestPointer() //cannot call pointer method on Data(struct (literal)
  1. 通过类型变量进行值调用和表达式调用,在这种情况下,使用值调用(method value)方式调用时编译器会进行自动转换,使用表达式调用(method expression)方式调用时编译器不会进行转换,会进行严格的方法集检查。例如:
type Data struct{}

func (Data)Testvalue(){}
func (*Data)TestPointer(){}

//声明一个类型变量a
var a Data struct{)(}

//表达式调用编译器不会进行自动转换
Data.Testvalue(a)
//Data.Testvalue(&a)
(*Data).TestPointer(&a)
//Data.TestPointer(&a) //type Data has no method TestPointer

//值调用编译器会进行自动转换
f := a.TestValue
f()

y:= (&a).TestValue//编译器帮助转换a.TestValue
y()

g:= a.TestPointer//会转换为(&a).TestPointer
9()

x := (&a).TestPointer
x()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[3\]中提到了一个名为pfring的工具,它是一种高性能的数据包捕获库,可以用于网络流量分析和监控。pfring的open函数是用来打开一个pfring句柄的函数,它可以接收一些参数来配置pfring的行为。具体来说,pfring的open函数可以接收以下参数: - device:指定要监听的网络设备的名称。 - snaplen:指定要捕获的数据包的最大长度。 - flags:指定一些标志来配置pfring的行为,比如是否启用混杂模式、是否启用零拷贝等。 通过调用pfring的open函数,可以打开一个pfring句柄,并开始捕获网络数据包。然后可以使用其他pfring提供的函数来处理和分析捕获到的数据包。pfring是一个非常强大和灵活的工具,可以用于各种网络流量分析和监控的场景。 #### 引用[.reference_title] - *1* *2* [Golang优秀开源项目汇总, 10大流行Go语言开源项目, golang 开源项目全集(golang/go/wiki/Projects), GitHub...](https://blog.csdn.net/weixin_34245082/article/details/86260411)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Linux网络性能优化相关策略](https://blog.csdn.net/qq_52358151/article/details/111967902)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值