Как преобразовать интерфейс в структуру - PullRequest
0 голосов
/ 05 марта 2019

Вот упрощенный код кеша.Предположим, что Container помещен в пакет, поэтому он не знает о Member.Хотя я хочу хранить экземпляры Member в контейнере, я сохраняю пустой экземпляр Member в контейнере как outerTypeContainer->GetMysql я заполняю новую переменную тестовыми значениями (но в реальном мире она заполняется данными базы данных, динамически).затем в функции Put я сохраняю данные в элементах в качестве кэша для следующего использования.В Get я получаю данные, хранящиеся в предметах.До этого все хорошо.Моя проблема в том, где я хочу преобразовать результат Get в тип Member m = res.(Member).Как я могу преобразовать его в экземпляр Member? Я нашел много вопросов по этому вопросу, но ни один из них не решил мою проблему

. Для более подробной информации: я хочу, чтобы Get возвращал данные с указателем того места, где они хранились.в пунктах.Так что, если я получу некоторую переменную того же члена, изменение в одной будет показано в других

package main

import (
    "fmt"
    "reflect"
)

type Member struct {
    Id     int
    Name   string
    Credit int
    Age    int
}

type Container struct {
    outerType interface{}
    items     map[string]*interface{}
}

func (cls *Container)GetMysql(s string, a int64) interface{}{
    obj := reflect.New(reflect.TypeOf(cls.outerType))
    elem := obj.Elem()
    //elem := reflect.ValueOf(o).Elem()
    if elem.Kind() == reflect.Struct {
        f := elem.FieldByName("Name")
        f.SetString(s)
        f = elem.FieldByName("Credit")
        f.SetInt(a)
    }
    return obj.Interface()
}

func (cls *Container)Get(value string) *interface{}{
    return cls.items[value]
}

func (cls *Container)Put(value string, a int64) {
    res := cls.GetMysql(value, a)
    cls.items[value] = &res
}

func main() {
    c := Container{outerType:Member{}}
    c.items = make(map[string]*interface{})
    c.Put("Jack", 500)
    res := c.Get("Jack")
    fmt.Println(*res)
    m := &Member{}
    m = res.(Member) // Here is the problem. How to convert ?
    fmt.Println(m)
}

1 Ответ

4 голосов
/ 05 марта 2019

Вам вряд ли стоит использовать указатель на интерфейс.Мой совет: никогда не используйте его, когда вам это нужно, вы будете знать.

Вместо этого, если вам нужен указатель на что-то (чтобы вы могли иметь один и тот же указатель в нескольких местах, и, таким образом, изменивуказывает значение где-то, это будет влиять на другие), "обернуть указатель" в значение интерфейса.

Поэтому сначала измените поле items, чтобы в нем вместо значений * указателей были указаны interface{} значения:

items map[string]interface{}

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

Далее измените Get(), чтобы вернуть interface{}:

func (cls *Container) Get(value string) interface{}{
    return cls.items[value]
}

А также в Put(), не берите адрес interface{}:

func (cls *Container) Put(value string, a int64) {
    res := cls.GetMysql(value, a)
    cls.items[value] = res
}

И вам нужно ввести *Member из значений, возвращаемых Get().

А теперь тестируем:

c := Container{outerType: Member{}}
c.items = make(map[string]interface{})
c.Put("Jack", 500)
res := c.Get("Jack")
fmt.Println(res)
m := res.(*Member) // Here is the problem. How to convert ?
fmt.Println(m)

Вывод (попробуйте на Go Playground ):

&{0 Jack 500 0}
&{0 Jack 500 0}

Теперь, если вы хотите изменить полеиз m:

m.Credit = 11

И затем получить значение из кэша:

fmt.Println(c.Get("Jack"))

Мы увидим измененное значение, даже если мы это сделалине звоните Put() (попробуйте на Go Playground ):

&{0 Jack 11 0}
...