如何在go中添加新方法到现有类型?

Dan*_*son 100 extension-methods go

我想在gorilla/mux路由和路由器类型上添加一个方便的util方法:

package util

import(
    "net/http"
    "github.com/0xor1/gorillaseed/src/server/lib/mux"
)

func (r *mux.Route) Subroute(tpl string, h http.Handler) *mux.Route{
    return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}

func (r *mux.Router) Subroute(tpl string, h http.Handler) *mux.Route{
    return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}
Run Code Online (Sandbox Code Playgroud)

但是编译器通知我

无法在非本地类型mux.Router上定义新方法

那我怎么做到这一点?我是否创建了一个具有匿名mux.Route和mux.Router字段的新结构类型?或者是其他东西?

jim*_*imt 146

正如编译器所提到的,您无法在另一个包中扩展现有类型.您可以按如下方式定义自己的别名或子包:

type MyRouter mux.Router

func (m *MyRouter) F() { ... }
Run Code Online (Sandbox Code Playgroud)

或者通过嵌入原始路由器:

type MyRouter struct {
    *mux.Router
}

func (m *MyRouter) F() { ... }

...
r := &MyRouter{router}
r.F()
Run Code Online (Sandbox Code Playgroud)

  • 如果您执行第一部分,那么如何将`mux.Router`实例强制转换为`MyRouter`s?例如,如果你有一个库返回`mux.Router`但你想使用你的新方法? (26认同)
  • 或者只是使用一个功能......? (8认同)
  • 执行此操作的@Paul需要覆盖String()和MarshalJSON()等函数 (4认同)

The*_*erk 95

我想通过@jimt给出的答案展开这里.这个答案是正确的,并帮助我彻底解决了这个问题.但是,对于我遇到麻烦的两种方法(别名,嵌入)都有一些警告.

注意:我使用父母和孩子这两个词,虽然我不确定这是最好的作文.基本上,parent是您要在本地修改的类型.Child是尝试实现该修改的新类型.

方法1 - 别名

type child parent
// or
type MyThing imported.Thing
Run Code Online (Sandbox Code Playgroud)
  • 提供对字段的访问.
  • 不提供对方法的访问.

方法2 - 嵌入(官方文档)

type child struct {
    parent
}
// or with import and pointer
type MyThing struct {
    *imported.Thing
}
Run Code Online (Sandbox Code Playgroud)
  • 提供对字段的访问.
  • 提供对方法的访问.
  • 需要考虑初始化.

摘要

  • 使用组合方法,嵌入的父级如果是指针则不会初始化.父母必须单独初始化.
  • 如果嵌入的父项是指针并且在初始化子项时未初始化,则将发生nil指针解除引用错误.
  • 别名和嵌入案例都提供对父级字段的访问.
  • 别名不允许访问父方法,但嵌入父方法.

您可以在以下代码中看到这一点.

在操场上的工作示例

package main

import (
    "fmt"
)

type parent struct {
    attr string
}

type childAlias parent

type childObjParent struct {
    parent
}

type childPointerParent struct {
    *parent
}

func (p *parent) parentDo(s string) { fmt.Println(s) }
func (c *childAlias) childAliasDo(s string) { fmt.Println(s) }
func (c *childObjParent) childObjParentDo(s string) { fmt.Println(s) }
func (c *childPointerParent) childPointerParentDo(s string) { fmt.Println(s) }

func main() {
    p := &parent{"pAttr"}
    c1 := &childAlias{"cAliasAttr"}
    c2 := &childObjParent{}
    // When the parent is a pointer it must be initialized.
    // Otherwise, we get a nil pointer error when trying to set the attr.
    c3 := &childPointerParent{}
    c4 := &childPointerParent{&parent{}}

    c2.attr = "cObjParentAttr"
    // c3.attr = "cPointerParentAttr" // NOGO nil pointer dereference
    c4.attr = "cPointerParentAttr"

    // CAN do because we inherit parent's fields
    fmt.Println(p.attr)
    fmt.Println(c1.attr)
    fmt.Println(c2.attr)
    fmt.Println(c4.attr)

    p.parentDo("called parentDo on parent")
    c1.childAliasDo("called childAliasDo on ChildAlias")
    c2.childObjParentDo("called childObjParentDo on ChildObjParent")
    c3.childPointerParentDo("called childPointerParentDo on ChildPointerParent")
    c4.childPointerParentDo("called childPointerParentDo on ChildPointerParent")

    // CANNOT do because we don't inherit parent's methods
    // c1.parentDo("called parentDo on childAlias") // NOGO c1.parentDo undefined

    // CAN do because we inherit the parent's methods
    c2.parentDo("called parentDo on childObjParent")
    c3.parentDo("called parentDo on childPointerParent")
    c4.parentDo("called parentDo on childPointerParent")
}
Run Code Online (Sandbox Code Playgroud)