【问题标题】:One type is coerced into another, can a method to determine the type of the receiver?一种类型被强制转换为另一种类型,可以确定接收者类型的方法吗?
【发布时间】:2011-09-22 23:42:30
【问题描述】:

如果类型T1T2是基于类型T,而类型T只存在于NewT1()NewT2(),那么函数func (*T) WhoAmI()有什么办法可以知道“真的”是T1 还是T2

包主 导入“fmt” 导入“反射” 类型 T 结构 { s 字符串 } func (v *T) WhoAmI() 字符串 { // 使用反射拉取类型名称 fmt.Println( reflect.TypeOf(v).Elem().Name() ) // 总是打印“T”! // todo: 如果我实际上是 T1 返回“T1” // todo: 否则,如果我实际上是 T2 返回“T2” } T1 T 型 func NewT1( s string ) T1 { return T1{ s } } T2 T 型 func NewT2( s string ) T2 { return T2{ s } } 功能主要(){ 变量 t1 = T1{“xyz”} var t2 = T2{“pdq”} s1 := ((*T)(&t1)).WhoAmI() // 想要返回 "T1" s2 := ((*T)(&t2)).WhoAmI() // 想要返回 "T2" fmt.Println(s1, s2) }

从技术上讲:

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

笼统地说:

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

【问题讨论】:

  • 我只是想到了reflect.TypeOf。不幸的是,该函数将返回您转换为的类型。你确定要投吗?如果您使用的是接口,则根本不需要强制转换。
  • 如果类型字段不同并且无法进行转换,那么转换可能会失败!?然后您可以尝试强制转换并捕获不可能的情况,从而知道它是哪种类型。在没有不同字段或方法的简单尝试中,这不起作用,因为强制转换显然有效。
  • 这样想。你能说出int(some_float)是什么类型吗?

标签: types go


【解决方案1】:

不,这是不可能的。从旧类型创建新类型与在基于类的语言中创建从父类继承的新类不同。在您的情况下,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 playground

上试试

T1 和 T2 都实现了接口 T,所以它们可以作为类型 T。

【讨论】:

  • 谢谢!对于接口,每种类型都必须实现接口的所有方法,这是我试图解决的问题
【解决方案2】:

埃文的回答很好。但是,有多种方法可以解决这个问题,更接近您的要求。

当你转换时,你实际上改变了类型,没有任何残留。 Go 只关心 current 类型是什么。

解决这个问题的一种方法就是编写一个函数。函数对于共享实现非常有用。一些面向对象的语言将它们视为不纯的,但它们不知道缺少什么(我在看你 public static void!)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-24
    相关资源
    最近更新 更多