douzhigan1687 2015-07-29 02:16 采纳率: 0%
浏览 41
已采纳

反映:设置指针的字段

I'm trying to do something like this:

Define structs with tags named env:

type Env struct {
     Port string `env:"PORT"`
}

Call some function which will get the environment variable names using os.Getenv and put set it in the struct.

Right now, I have this:

package main

import (
    "fmt"
    "os"
    "reflect"
)

func ParseEnv(t interface{}, v interface{}) {
    it := reflect.TypeOf(t)
    for i := 0; i < it.NumField(); i++ {
        field := it.Field(i)
        value := os.Getenv(field.Tag.Get("env"))
        if value == "" {
            continue
        }
        reflect.ValueOf(v).Elem().FieldByName(field.Name).SetString(value)
    }
}

type Env struct {
    Port        string `env:"PORT"`
    DatabaseURL string `env:"DATABASE_URL"`
}

func main() {
    os.Setenv("PORT", "8080")
    os.Setenv("DATABASE_URL", "postgres://user:pass@host:5432/my-db")
    env := Env{}
    ParseEnv(env, &env)
    fmt.Println(env)
}

http://play.golang.org/p/b8uPPVo4aV

But, as you can see, I have to pass both the reference and the pointer to my function.

While this works, it is very ugly (at least I think it is).

If I try to pass the pointer only, I can't get the type right (because it will be an *interface{}) and, if I pass only the reference, I can't set the values using reflect (even if I could, it would not work).

Is there a sane way of doing this?

  • 写回答

1条回答 默认 最新

  • doukun8670 2015-07-29 02:58
    关注

    Below is a "saner" way of doing what you want. You will notice that, instead of passing in two copies of the struct, we only need a pointer to the struct.

    func ParseEnv(val interface{}) {
        ptrRef := reflect.ValueOf(val)
        if ptrRef.Kind() != reflect.Ptr {
            panic("pointer to struct expected")
        }
        ref := ptrRef.Elem()
        if ref.Kind() != reflect.Struct {
            panic("pointer to struct expected")
        }
        refType := ref.Type()
        for i := 0; i < refType.NumField(); i++ {
            field := refType.Field(i)
            value := os.Getenv(field.Tag.Get("env"))
            if value == "" {
                continue
            }
            ref.Field(i).SetString(value)
        }
    }
    

    The above function should be invoked in the following way:

    ParseEnv(&env)
    

    Example: https://play.golang.org/p/_BwWz2oUql

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?