В Go один тип приведен к другому, может ли метод определить тип получателя? - PullRequest
5 голосов
/ 26 июня 2011

Если типы T1 и T2 основаны на типе 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?

, говоря в общем:

другими словами, если один тип основан на другом, еслипеременная производного типа приводится в базовый тип для запуска метода, может ли этот метод узнать реальный тип получателя, который его вызвал?

Ответы [ 2 ]

8 голосов
/ 27 июня 2011

Нет, это невозможно.Создание нового типа из старого не похоже на создание нового класса, который наследуется от родительского класса на языке классов.В вашем случае 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.

4 голосов
/ 12 июля 2011

Ответ Эвана хорош. Однако существует несколько способов решения этой проблемы, которые ближе к тому, что вы искали.

Когда вы конвертируете, вы фактически меняете типы, в этом нет ничего остаточного. Go заботится только о том, что является current type.

Один из способов обойти это просто написать функцию. Функции очень полезны для совместной реализации. Некоторые объектно-ориентированные языки отбрасывают их как нечистые, но они не знают, чего им не хватает (я смотрю на вас, публичная статическая пустота!).

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

Плюс в том, что вам не нужно делать никакой дополнительной работы. Метод имеет доступ как к т, так и к себе, поэтому он обладает лучшим из обоих миров. Однако это становится частью вашего интерфейса, что немного неудобно.

Вы также можете сделать это поле:

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
}

Теперь любые методы в T или T1 могут определить, как был изначально создан объект, проверяя self.

Вы можете продолжать конвертировать туда и обратно, чтобы получить методы, или вы можете использовать функцию под названием встраивание:

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

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

Как видите, вы можете позвонить T.M через t или t1. Однако имейте в виду, что T.M всегда будет видеть только T, независимо от того, как вы его называете (t или t1). Вам нужно будет использовать одну из вышеуказанных стратегий, чтобы T.M мог видеть T1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...