一种类型被强制转换为另一种类型,是否可以通过方法来确定接收者的类型?

发布于 2024-11-17 17:15:45 字数 1219 浏览 2 评论 0原文

如果类型 T1T2 基于类型 T,并且类型 T 仅从 T 中存在>NewT1() 或 NewT2(),函数 func (*T) WhoAmI() 有什么方法可以知道它是否“真的”是一个T1 或T2?

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 的方法的角度回收知识?

一般来说:换句话说

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

If types T1 and T2 are based on type T, and type T only comes into existence from a NewT1() or NewT2(), is there any way a function func (*T) WhoAmI() can know whether it "really" is a T1 or T2?

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)
}

to speak technically:

once t1 type T1 is coerced into type T so func (*T) WhoAmI() can be called, does t1 completely lose the fact that its type is really T1? if not, how do we reclaim the knowledge from the perspective of a method receiving type T?

to speak generally:

in other words, if one type is based on another, if a variable of the derived type is coerced into the base type to run a method, can that method learn the real type of the receiver who called it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我很OK 2024-11-24 17:15:45

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

No, it's not possible. Creating a new type from an old one is not like creating a new class that inherits from a parent class in an class-based language. In your case T knows nothing about either T1 or T2 and if you're calling the WhoAmI method you have a receiver of type T by definition.

Your design might work better with an interface. Try something more like this:

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" }

Try it on the Go playground

T1 and T2 both implement the interface T, so they can be used as type T.

薄凉少年不暖心 2024-11-24 17:15:45

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

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

解决这个问题的一种方法是编写一个函数。函数对于共享实现非常有用。一些面向对象的语言将它们视为不纯的,但它们不知道它们缺少什么(我正在看着你 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()

如您所见,您可以通过tTM代码>或<代码>t1。但是,请记住,TM 始终只会看到 T,无论您如何调用它(tt1< /代码>)。您必须使用上述策略之一,TM 才能看到 T1

Evan's answer is good. However, there are multiple ways to solve this problem that are closer to what you were looking for.

When you convert, you actually change types, there's nothing residual. Go only cares what the current type is.

One approach to get around this is just to write a function. Functions are very useful for having shared implementation. Some object oriented languages threw them out as being impure, but they don't know what they're missing (I'm looking at you 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"
}

Now you don't have to convert the value in order to call the method/function. Of course, if you're going to do a type switch and do something different for each type, you might as well just write a different method for each type.

To make it a method, you could do:

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

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

This way, you don't need to reimplement the method.

If you really want T to know itself, give it a self! There are two ways of doing this. One is as a parameter:

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

The upside of this is that you don't need to do any extra work. The method has access to both t and self, so it has the best of both worlds. However, this becomes part of your interface, which is a little awkward.

You could also make it a field:

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
}

Now, any methods on a T or T1 can tell what the object was originally created as by checking self.

You can keep converting back and forth to get methods, or you can use a feature called embedding:

type T struct{}
func (t *T) M() {}

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

As you can see, you can call T.M via t or t1. However, keep in mind that T.M will always only see a T, regardless of what you call it on (t or t1). You'll have to use one of the above strategies in order for T.M to be able to see a T1.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文