首页 文章

在Go中,一种类型被强制转换为另一种类型,可以用一种方法来确定接收器的类型吗?

提问于
浏览
5

如果类型 T1T2 基于类型 T ,并键入 T 只开始存在从 NewT1()NewT2() ,有没有什么办法的功能 func (*T) WhoAmI() 可以知道是否"really"是 T1T2

package main
import "fmt"
import "reflect"

type T struct { s string }
func (v *T) WhoAmI() string {

        // pull type name with reflect  
        fmt.Println( reflect.TypeOf(v).Elem().Name() )  // always prints "T"!

        // todo: if I am actually T1
        return "T1"
        // todo: else if I am actually T2
        return "T2"
}

type T1 T
func NewT1( s string ) T1 { return T1{ s } }

type T2 T
func NewT2( s string ) T2 { return T2{ s } }

func main() {
        var t1 = T1{ "xyz" }
        var t2 = T2{ "pdq" }
        s1 := ((*T)(&t1)).WhoAmI()      // would like to return "T1"
        s2 := ((*T)(&t2)).WhoAmI()      // would like to return "T2"
        fmt.Println( s1, s2 )
}

从技术上讲:

一旦 t1 类型 T1 被强制转换为类型 T 所以可以调用 func (*T) WhoAmI()t1 完全失去了它的类型真的 T1 的事实吗?如果没有,我们如何从接收类型 T 的方法的角度回收知识?

一般来说:

换句话说,如果一个类型基于另一个类型,如果派生类型的变量被强制转换为基类型以运行方法,那么该方法是否可以学习调用它的接收方的实际类型?

2 回答

  • 5

    不,这是不可能的 . 从旧类型创建新类型与创建从基于类的语言中的父类继承的新类不同 . 在你的情况下,T对T1或T2一无所知,如果你正在调用WhoAmI方法,你有一个T型接收器的定义 .

    您的设计可能会更好地使用界面 . 尝试更像这样的东西:

    type T interface {
        WhoAmI() string
    }
    
    type T1 struct {
        s string
    }
    
    func (t *T1) WhoAmI() string { return "T1" }
    
    type T2 struct {
        s string
    }
    
    func (t *T2) WhoAmI() string { return "T2" }
    

    在Go游乐场试试吧

    T1和T2都实现接口T,因此它们可以用作类型T.

  • 3

    埃文的回答很好 . 但是,有多种方法可以解决这个问题,更接近您所寻找的问题 .

    当你转换时,你实际上改变了类型,没有任何残余 . Go只关心当前的类型 .

    解决这个问题的一种方法就是编写一个函数 . 函数对于共享实现非常有用 . 一些面向对象的语言将它们抛弃为不纯净,但是它们不知道它们缺少什么(我正在看着你的公共静态虚空!) .

    func WhoAmI(v interface{}) string {
        switch v.(type) {
        case *T: return "*T"
        case *T1: return "*T1"
        case *T2: return "*T2"
        }
    
        return "unknown"
    }
    

    现在您不必转换值以调用方法/函数 . 当然,如果您要进行类型切换并为每种类型执行不同的操作,您可以为每种类型编写不同的方法 .

    要使它成为一种方法,您可以:

    type T struct { s string }
    func (t *T) WhoAmI() string { return WhoAmI(t) }
    
    type T1 T
    func (t1 *T1) WhoAmI() string { return WhoAmI(t1) }
    

    这样,您就不需要重新实现该方法 .

    如果你真的想 T 知道自己,那就自己动手吧!有两种方法可以做到这一点 . 一个是参数:

    func (t *T) WhoAmI(self interface{}) string { ... }
    ...
    fmt.Println(t.WhoAmI(t))
    fmt.Println(((*T)(t1)).WhoAmI(t1))
    

    这样做的好处是你不需要做任何额外的工作 . 该方法可以访问t和self,因此它具有两全其美的优点 . 但是,这成为您界面的一部分,这有点尴尬 .

    你也可以把它变成一个字段:

    type T struct { self interface{} }
    func NewT() *T {
        t := new(T)
        t.self = t
        return t
    }
    
    type T1 T
    func NewT1() *T1 {
        t1 := new(T1)
        t1.self = t1
        return t1
    }
    

    现在, TT1 上的任何方法都可以通过检查 self 来判断最初创建对象的内容 .

    您可以继续来回转换以获取方法,或者您可以使用称为嵌入的功能:

    type T struct{}
    func (t *T) M() {}
    
    type T1 struct { T }
    ...
    var t T
    var t1 T1
    t.M()
    t1.M()
    

    如您所见,您可以通过 tt1 调用 T.M . 但是,请记住 T.M 始终只会看到 T ,无论您调用它( tt1 ) . 您必须使用上述策略之一才能让 T.M 能够看到 T1 .

相关问题