【问题标题】:How can I access the fields of an interface in Go?如何访问 Go 中接口的字段?
【发布时间】:2016-04-11 16:20:54
【问题描述】:

我正在尝试这样做:

if event.Type == sdl.QUIT {
    utils.Running = false
}

但我不能,因为当我尝试构建时,我得到了这个错误:

 ./mm.go:11: event.Type undefined (type sdl.Event has no field or method Type)

这是我正在尝试使用的库的相关源代码:

type Event interface{}    

type CEvent struct {
    Type uint32
    _    [52]byte // padding
}

type CommonEvent struct {
    Type      uint32
    Timestamp uint32
}

// WindowEvent (https://wiki.libsdl.org/SDL_WindowEvent)
type WindowEvent struct {
    Type      uint32
    Timestamp uint32
    WindowID  uint32
    Event     uint8
    _         uint8 // padding
    _         uint8 // padding
    _         uint8 // padding
    Data1     int32
    Data2     int32
}

如您所见,所有其他事件都有字段Type。我怎样才能访问它?

解决方案

这就是我最终在this SDL2 binding for Go 中投票的方式,以防有人想知道:

func PollEvents() {
    for {
        if event := sdl.PollEvent(); event != nil {
            switch event.(type) {
            case *sdl.QuitEvent:
                utils.Running = false
            }
        } else {
            break
        }
    }
}

【问题讨论】:

    标签: go struct interface


    【解决方案1】:

    实际上你做不到。接口只定义了一个类型上可用的方法集,它们对公开字段没有任何作用。在您的情况下,我建议您进行类型切换。它看起来有点像这样;

         switch v := myInstance.(type) {
                    case CEvent:
                            fmt.Println(v)
                    case CommonEvent:
                            fmt.Println(v)
                    case WindowEvent:
                            fmt.Println(v)
                    default:
                            fmt.Println("unknown")
            }
    

    您可能希望根据在此之后对实例所做的操作来稍微不同地构建代码,但这为您提供了基本概念。您还可以使用单一类型进行类型断言,例如; v, err := myInstance.(CommonEvent) 但我怀疑它在这里是否有效。如果myInstance 的类型不是CommonEvent,它也会返回错误,因此这并不是确定类型和接口实例可能是什么的最佳方法。

    【讨论】:

    • 我是新手,但我不知道这种方式可行吗? play.golang.org/p/2cxLDoabfs
    • @william.taylor.09 它工作正常,您的示例正确生成“未知”,因为您传递的是 *WindowEvent - 这与 WindowEvent 的类型不同,并且没有案例。这是一个修改后的示例,可以满足您的期望; play.golang.org/p/ZEkf3tKI53
    • 这似乎有效,但如果我不使用v,我会在编译时出现非常烦人的variable declared but not used 错误。除了每个 case SomeEvent: 行之外,我不想使用 v 。有没有办法避免不得不使用它?
    • @gragas 我必须查看您的其余代码,但我的猜测是您在 switch 上方声明 v,而不是像在 switch 语句中那样声明和赋值部分上面的例子。如果v 像上面的示例一样在 switch 语句中声明,那么它的范围仅限于 switch 语句,因此您不必在任何地方使用它,而是在 case 语句中。
    • @evanmcdonnal 我基本上是在运行您的示例,但在任何情况下我都不使用v。因为一旦我知道了事件的类型,我就不需要使用它了。这可能是一个golang错误吗?这是导致此问题的代码要点:gist.github.com/gragas/696a16cbc6bd2cee201d82f75c12fbb6
    【解决方案2】:

    您需要知道类型。假设我们知道这是一个 CEvent:

    cEvent, ok := Event.(CEvent)
    if !ok {
        // You lied, not a CEvent
        return
    }
    
    // Otherwise, you can get the type!
    fmt.Println(cEvent.Type)
    

    当然,如果你知道类型,你可以保持类型断言直到你做对了。否则,抛出错误,返回默认值等:

    func getType(i interface{}) uint32 {
        cEvent, ok := i.(CEvent)
        if ok {
            return cEvent.Type
        }
    
        commonEvent, ok := i.(CommonEvent)
        if ok {
            return commonEvent.Type
        }
    
        // Etc
    
        return <default>
    }
    

    【讨论】:

      【解决方案3】:

      您可能会花费大量时间进行反射调用或尝试猜测类型或使用类型开关。

      或者您可以只定义一个带有返回所需信息的函数的接口。

      例如你可以这样做

      type Event interface {
          GetCommonEvent() *CommonEvent
          GetWindowEvent() *WindowEvent
      }
      

      【讨论】:

        猜你喜欢
        • 2014-11-19
        • 2014-03-14
        • 2021-10-17
        • 2018-03-13
        • 1970-01-01
        • 1970-01-01
        • 2016-01-14
        • 2021-04-24
        • 1970-01-01
        相关资源
        最近更新 更多