Использование отражения для присвоения типизированного значения

Я работаю над одним из наших системных приложений, особенно над битами обработки файла конфигурации. В настоящее время у нас есть 3 разных места, где может храниться файл конфигурации, и это может быть расширено позже. Я пытаюсь упростить способ добавления нового управляемого поля.

Решение, которое у меня есть до сих пор, выглядит примерно так:

package main

import (
    "reflect"
    "strconv"
    "strings"
)

type Datastore interface{}

type MyInt struct {
    intVal int
}

func NewMyInt(key string, dv int, db *Datastore) *MyInt {
    // Do something here to construct MyInt
    return &MyInt{intVal: dv}
}

type Config struct {
    myInts map[string]*MyInt

    // Tag is of form "<key in DB>:<default value>"
    Value1 MyInt "value1_key:12345"
    Value2 MyInt "value2_key:54321"
}

func NewConfig(db *Datastore) *Config {
    c := &Config{
        myInts: make(map[string]*MyInt),
    }

    cType := reflect.TypeOf(c)

    for i := 0; i < cType.NumField(); i++ {
        f := cType.Field(i)
        if f.Name == "myInts" {
            continue
        }

        tag := string(f.Tag)
        fields := strings.Split(tag, ":")

        switch f.Type.Name() {
        case "myInt":
            intVal, _ := strconv.Atoi(fields[1])
            val := NewMyInt(fields[0], intVal, db)
            c.myInts[fields[0]] = val

            // How do I set the i'th field to this newly constructed value?
        }
    }

    return c
}

Пока мне просто не хватает этого кусочка для выполнения задания.


person Ken P    schedule 03.08.2020    source источник
comment
Чтобы узнать, как использовать reflect для установки поля структуры, см. ответ peterSO на этот вопрос. Однако обратите внимание, что использование name типа для определения типа, который нужно установить, является... по крайней мере необычным; обычно вы включаете type и используете case reflect.TypeOf(...), как в примере. Если у вас есть набор, я предполагаю, что имя X является типом Y, вам не нужно проходить по полям по одному, как в упрощенной версии этого примера.   -  person torek    schedule 04.08.2020


Ответы (1)


Для этого вопроса вы можете попробовать


func NewConfig(db *Datastore) *Config {
    c := &Config{
        myInts: make(map[string]*MyInt),
    }

    cType := reflect.TypeOf(c).Elem()  // have to use Elem() to get actual value
    cValue := reflect.ValueOf(c).Elem()

    for i := 0; i < cType.NumField(); i++ {
        f := cType.Field(i)
        if f.Name == "myInts" {
            continue
        }

        tag := string(f.Tag)
        fields := strings.Split(tag, ":")
        switch f.Type.Name() {
        case "MyInt":
            intVal, _ := strconv.Atoi(fields[1])
            val := NewMyInt(fields[0], intVal, db)
            c.myInts[fields[0]] = val

            // How do I set the i'th field to this newly constructed value?
            cValue.Field(i).Set(reflect.ValueOf(val).Elem())
        }

    }
    fmt.Println(c.Value1.intVal, c.Value2.intVal)

    return c
}

person richard_yang    schedule 04.08.2020