【问题标题】:panic: reflect: call of reflect.Value.Call on zero Value恐慌:反射:调用反射.Value.Call on 零值
【发布时间】:2022-04-07 11:47:00
【问题描述】:

我正在尝试使用反射来根据用户输入进行动态函数调用。我正在收集这样的用户输入:

func main() {
    for {
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("> ")
        command, _ := reader.ReadString('\n')
        runCommandFromString(command)
    }
}

这就是命令的解析方式

func stringCommandParser(cmd string) *Command {
    cmdParts := strings.Split(
        strings.TrimSpace(cmd),
        " ",
    )
    return &Command{
        Name: cmdParts[0],
        Args: cmdParts[1:],
    }
}

func runCommandFromString(cmd string) {
    command := stringCommandParser(cmd)

    c := &Commander{}
    f := reflect.ValueOf(&c).MethodByName(command.Name)
    inputs := []reflect.Value{reflect.ValueOf(command)}
    f.Call(inputs)
}

commands.go 文件如下所示

type Command struct {
    Name string
    Args []string
}

type Commander struct {}

func (c Commander) Hello(cmd Command) {
    fmt.Println("Meow", cmd.Args)
}

当我运行程序时,我得到一个提示,然后我运行一个名为“Hello”的命令,并带有一个名为“world”的参数。我希望出现“喵 [世界]”或类似的东西。像这样:

> Hello world
Meow world

相反,我得到的是一个看起来像这样的恐慌:

> Hello world
panic: reflect: call of reflect.Value.Call on zero Value

goroutine 1 [running]:
panic(0x123360, 0xc420014360)
    /Users/parris/.gvm/gos/go1.7.4/src/runtime/panic.go:500 +0x1a1
reflect.flag.mustBe(0x0, 0x13)
    /Users/parris/.gvm/gos/go1.7.4/src/reflect/value.go:201 +0xae
reflect.Value.Call(0x0, 0x0, 0x0, 0xc420055e00, 0x1, 0x1, 0x0, 0x0, 0x0)
    /Users/parris/.gvm/gos/go1.7.4/src/reflect/value.go:300 +0x38
main.runCommandFromString(0xc42000c8a0, 0xc)
    /Users/parris/Projects/distro/main/utils.go:26 +0x1a0
main.main()
    /Users/parris/Projects/distro/main/main.go:14 +0x149
exit status 2

我该如何解决这个问题?还有,发生了什么?

【问题讨论】:

    标签: go reflection


    【解决方案1】:

    您可能想在使用之前检查reflect.ValueOf 返回的内容。

    在这种情况下,您将得到一个 nil 值,因为 reflect 无法看到未导出的函数。 hello 以小写字母开头,因此不会被导出。

    另外,你在这里提出了太多的指示,我怀疑你需要做reflect.ValueOf(c)。你同时在做和调试太多的事情。制作一个简单的工作示例并从那里继续调试。

    这对我有用:https://play.golang.org/p/Yd-WDRzura

    【讨论】:

    • 更新了我的代码。将 hello 更改为 Hello 不走运。注意:我也尝试通过硬编码直接调用“Hello”。我什至尝试像这样替换输入变量:inputs := []reflect.Value{},只是让你好不要期望任何参数。
    • @Parris 更新了我的答案。这个网站上的人们要求提供一个简单、可测试的自包含示例的原因是为了避免同时调试太多问题。您可能在代码中还有更多问题。无法真正知道,因为它不可测试。
    • Soo 对我的回答是“你到处都在使用指针来处理不需要指针的东西”。在这种情况下,展示完整的示例很有帮助,因为孤立的东西是有效的。我是新来的,我不知道我不知道什么。
    【解决方案2】:

    只需对您的代码进行两次更改。 首先将reflect.ValueOf(&c) 更改为reflect.ValueOf(c) 其次将reflect.ValueOf(command)改为 reflect.ValueOf(*command)

    这是代码workingcode

    【讨论】:

      【解决方案3】:

      调用 Kind() 进行检查

      type Header struct {
          Token string
      }
          
      type Request struct {
          Header 
      }   
      
      
      func requestedToken(request interface{}) string {
          requestValue := reflect.ValueOf(request)
          header := requestValue.FieldByName("Header12")
          if header.Kind() == 0 {
              return "" //Return here
          }
          token := header.FieldByName("Token")
          if token.Kind() == 0 {
              return ""
          }
          return token.String()
      }
      

      【讨论】:

        【解决方案4】:

        您需要将runCommandFromString 更改为RunCommandFromString

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-12-06
          • 2021-09-12
          • 2021-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-23
          • 2014-07-02
          相关资源
          最近更新 更多