使用反射解码参数

  • 本文关键字:参数 解码 反射 go
  • 更新时间 :
  • 英文 :


这是一个函数和参数的后续问题。

给定以下接口变量。

var args interface{}

假设它包含以下字节:

[[74 111 104 110] [32 97 110 100 32 74 97 110 101]]

即。两个字符串" John"和Jane

和通过MethodByName获得的函数值

f := reflect.ValueOf(s.functions).MethodByName("Hello")
if f.IsValid() {
  val := reflect.ValueOf(args)
  //  Do some kind of conversion...
  result := f.Call(val) // This won't compile. Wrong type.  How do I massage args of type interface{} into what's expected by the call.
}

我不特别关心它是否失败。我将通过恢复捕获电话的故障。

这里有更多细节:

var req struct {
    Ver      int
    MsgID    int
    FuncName string
    Args     interface{}
}
dec := codec.NewDecoder(frame, s.h)
err = dec.Decode(&req)
if err != nil {
    s.log.Println(err)
    break
}
fmt.Println("New Msg:")
fmt.Printf(" ver  : %dn", req.Ver)
fmt.Printf(" id   : %dn", req.MsgID)
fmt.Printf(" func : %sn", req.FuncName)
f := reflect.ValueOf(s.functions).MethodByName(req.FuncName)
if f.IsValid() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Function is: ", req.FuncName)
    var callArgs []reflect.Value
    args := reflect.ValueOf(req.Args)
    t := f.Type()
    for i := 0; i < t.NumIn(); i++ {
        t := t.In(i)
        v := reflect.New(t).Elem()
        if i < args.Len() {
            // Convert arg to type of v and set.
            arg := args.Index(i)
            switch t.Kind() {
            case reflect.String:
                v.SetString(string(reflect.Value.Bytes(arg)))
            case reflect.Slice:
                if t.Elem() == reflect.TypeOf(byte(0)) {
                    v.SetBytes(reflect.Value.Bytes(arg))
                } else {
                    panic("not supported")
                }
            case reflect.Int:
                //i, err := strconv.ParseInt(string(arg), 10, 0)
                // if err != nil {
                //  panic("bad int")
                // }
                // v.SetInt(i)
            default:
                panic("not supported")
            }
        }
        // Collect arguments for the call below.
        callArgs = append(callArgs, v)
    }
    result := f.Call(callArgs)
    fmt.Println(result)
    val := reflect.ValueOf(req.Args)
    a := []reflect.Value{val}
    r := f.Call(a)
    fmt.Println("Returned", r[0], r[1])
}

输出:

新味精: VER:2 ID:1 功能:你好功能是:你好在f反射中恢复:接口值

上的call of call of Reflect.value.bytes

注意:这是RPC API。我有一个函数名称(请参阅问题的链接)和一些作为数组传递的参数。我的示例中的参数是字符串,但您可以将任何可以传递给函数的任何事情。这是功能所需的一切。这是通用的。

例如。

你好(名称...字符串)字符串

add(n ... int)int

dosomething(a string,b int,c bool)

etc

即。我正在解除论点,但我不知道它们是什么。除了我确实知道它们会被转移到一个切片中扔进具有interface{}类型的变量args,我希望这是有道理的

尝试以下内容。

基本思想是创建一个[]反射。通过对函数参数进行循环循环来反映呼叫的值。对于每个参数,将传入参数类型转换为函数调用中预期的类型。

var req = struct {
    Ver      int
    MsgID    int
    FuncName string
    Args     interface{}
}{
    Args: []interface{}{[]byte("John"), "Jane", 123, "456"},
}
args := req.Args.([]interface{})
var funcs Funcs
f := reflect.ValueOf(funcs).MethodByName("Hello")
var callArgs []reflect.Value
t := f.Type()
// For each function argument ...
for i := 0; i < t.NumIn(); i++ {
    t := t.In(i)
    v := reflect.New(t).Elem()
    if i < len(args) {
        // Convert arg to type of v and set.
        arg := args[i]
        switch t.Kind() {
        case reflect.String:
            switch arg := arg.(type) {
            case string:
                v.SetString(arg)
            case []byte:
                v.SetString(string(arg))
            default:
                panic("not supported")
            }
        case reflect.Slice:
            if t.Elem() != reflect.TypeOf(byte(0)) {
                panic("not supported")
            }
            switch arg := arg.(type) {
            case string:
                v.SetBytes([]byte(arg))
            case []byte:
                v.SetBytes(arg)
            default:
                panic("not supported")
            }
        case reflect.Int:
            switch arg := arg.(type) {
            case int:
                v.SetInt(int64(arg))
            case string:
                i, err := strconv.ParseInt(arg, 10, 0)
                if err != nil {
                    panic("bad int")
                }
                v.SetInt(i)
            default:
                panic("not supported")
            }
        default:
            panic("not supported")
        }
    }
    // Collect arguments for the call below.
    callArgs = append(callArgs, v)
}
result := f.Call(callArgs)
fmt.Println(result)

该片段包括字符串,[]字节和int类型的转换。可以添加其他类型的转换。

游乐场示例

我错了 - 问题是使用 []而不是单数值:

  val := reflect.ValueOf(args)
  //  Do some kind of conversion...
  a := []reflect.Value{val}
  result := f.Call(a) 

https://play.golang.org/p/aqqama-ljv

最新更新