当导入包中定义了结构体时如何使用 go 接收器

Ale*_*ner 4 linux network-programming go netlink

目前正在使用vishvananda/netns尝试从特定网络名称空间提取路由的包。

Handle当我请求特定网络命名空间的“句柄”时,会返回一个已定义的结构。像这样:

func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error)

这是需要该句柄的函数的接收器参数(?),

func (h *Handle) LinkList() ([]Link, error)

我是新手,不知道如何将这些联系在一起。我被困住了:

func (h *Handle) showInts() {
  int, err := h.netlink.LinkList()
  if err != nil {
      log.Fatal(err)
  }

  for i, r := range int {
      log.Printf("%d: %s", i, r.Attrs().Name)
  }
}


func main() {
    ints, err := netlink.LinkList()
    if err != nil {
        log.Fatal(err)
    }
    for i, r := range ints {
        log.Printf("%d: %s", i, r.Attrs().Name)
    }

    pid, err := netns.GetFromPid(9097)
    if err != nil {
        log.Fatal(err)
    }

    netlink.NewHandleAt(pid)
    showInts()
}
Run Code Online (Sandbox Code Playgroud)

Eli*_*gem 8

更新

在编写原始答案时,触及了很多内容,但没有任何清晰的结构,因此这里有一个更结构化的版本:

根据您实际询问的内容(即“如何将接收器函数/方法添加到导出类型”“接收器函数到底是什么”),答案如下:

如何将接收器函数添加到导出类型?

很简单,就像处理任何其他类型一样。事实上,你很接近。这不起作用:

func (h *Handler) showInts() {}
Run Code Online (Sandbox Code Playgroud)

因为您要向包中Handler的类型添加一个方法。如果你有一个函数,那就是包。您正在尝试将其添加到类型中。在这种情况下,这将起作用:mainmainnetlink.Handler

func (h *netlink.Handler) showInts(){}
Run Code Online (Sandbox Code Playgroud)

netlink.Handler毕竟,该类型位于您的主包中...但是,这是行不通的。编译器会拒绝编译,告诉你:“Cannot Define new method on non-local type”。不过,通过创建一个新类型并在其中添加方法可以轻松缓解这一问题:

type MyHandler netlink.Handler
func (h *MyHandler) showInts(){}
Run Code Online (Sandbox Code Playgroud)

尽管如此,代码中的最后两行让我觉得是错误的。鉴于NewHandleAt返回(*Handle, error), 并且netlink.Handle是接收者参数,正确的方法是:

var mh *MyHandle
if h, err := netlink.NewHandleAt(pid); err != nil {
    log.Fatal(err) // something went wrong
} else {
    mh = (*MyHandle)(h)
}
mh.showInts() // call showInts on mh, which is of type *MyHandle
Run Code Online (Sandbox Code Playgroud)

事实上,您已将外部类型“包装”在自定义类型中,这确实意味着您会发现自己多次转换相同的东西。假设netlink.Handle有一个Test方法,并且您想在内部调用它showInts

func (h *MyHandle) showInts() {
    nh := (*netlink.Handle)(h) //cast required
    nh.Test()
}
Run Code Online (Sandbox Code Playgroud)

我还将 varname 从 更改为pidnsh其他名称,因为它是 a NsHandle,而不是 apid毕竟......


什么是接收者参数?

因为你写了这个:

这是需要该句柄的函数的接收器参数(?),

我的印象是您并不完全清楚接收者参数是什么。简而言之,它就像一个函数参数,但它不是一个刚刚传递给函数的参数,而是一个保存调用该函数的对象/值的参数。基本上,它是调用函数/方法的“实例” 。将其视为this许多 OOP 语言中的关键字:

func (h *MyHandle) showInts() {
    return
}
Run Code Online (Sandbox Code Playgroud)

在像 C++ 这样的东西中

class MyHandle : Handle
{
    public:
        void showInts(void) { return; } // replace h with this
}
Run Code Online (Sandbox Code Playgroud)

然而,也存在显着差异:

  • 接收者参数可以是指针,也可以是值 - 如果是值接收者,该方法无法修改接收者值
  • 不存在私有、公共或受保护这样的东西……至少在传统的面向对象方式中不存在
  • ...

有相当多的差异,也许可以考虑通过 golang 之旅。关于 go 方法的内容可以在这里找到


其他问题/奇怪的事情

再次查看您的代码后,我真的不确定这是否正确:

h.netlink.LinkList()
Run Code Online (Sandbox Code Playgroud)

在您的main函数中,您调用netlink.LinkList(). h是一个*netlink.Handler. 如果您需要调用该netlink.LinkList函数,那么它很可能不是h.netlink.LinkList您想要做的。相反,您应该简单地调用. 假设您首先需要调用该函数。netlink.LinkList()

既然您已经在函数中调用了它main,为什么不将它作为参数传递呢?

//in main:
ints, err := netlink.LinkList()
//...
h.showInts(ints)

func (h *MyHandle)showInts(ll []netlink.Link) {
}
Run Code Online (Sandbox Code Playgroud)