恐慌:反射:在零值上调用反射.Value.Call。

huangapple go评论95阅读模式
英文:

panic: reflect: call of reflect.Value.Call on zero Value

问题

我正在尝试使用反射来根据用户输入进行动态函数调用。我正在收集用户输入,代码如下:

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" 的参数。我期望会出现 "Meow [World]" 或类似的输出。就像这样:

> Hello world
Meow world

但实际上我得到了一个 panic,错误信息如下:

> 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

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

英文:

I'm trying to use reflection in order to make a dynamic function call based on user input. I'm collecting user input like such:

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

This is how the command is parsed out

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)
}

The commands.go file looks like this

type Command struct {
    Name string
    Args []string
}

type Commander struct {}

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

When I run the program, I get a prompt and I run a command called "Hello" with a param called "world". I would expect "Meow [World]" or something like that to appear. Like this:

> Hello world
Meow world

Instead what I get is a panic that looks like this:

> 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

How can I fix this? Also, what is happening?

答案1

得分: 9

在使用reflect.ValueOf之前,你可能需要检查它返回的内容。

在这种情况下,你得到了一个空值,因为反射无法识别未导出的函数。hello以小写字母开头,因此未导出。

此外,你在这里创建了太多的指针,我怀疑你需要执行reflect.ValueOf(c)。你同时进行了太多的调试工作。先创建一个简单的可工作示例,然后从那里继续进行调试。

这个示例对我有效:https://play.golang.org/p/Yd-WDRzura

英文:

You might want to check what reflect.ValueOf returns before using it.

In this case you're getting a nil value because reflect can not see functions that aren't exported. hello starts with a lower case letter and therefore isn't exported.

Also, you're making too many pointers here, I suspect that you need to do reflect.ValueOf(c). You're doing and debugging too many things at the same time. Make a simple working example and continue debugging from there.

This works for me: https://play.golang.org/p/Yd-WDRzura

答案2

得分: 8

只需在你的代码中做两个更改。
第一个更改将reflect.ValueOf(&c)更改为reflect.ValueOf(c)
第二个更改将reflect.ValueOf(command)更改为reflect.ValueOf(*command)

这是代码workingcode

英文:

Just do two changes in your code.
First change reflect.ValueOf(&c) to reflect.ValueOf(c)
Secondly change reflect.ValueOf(command) to reflect.ValueOf(*command)

This is the code workingcode

答案3

得分: 2

调用Kind()函数来检查

type Header struct {
	Token string
}

type Request struct {
	Header
}

func requestedToken(request interface{}) string {
	requestValue := reflect.ValueOf(request)
	header := requestValue.FieldByName("Header")
	if header.Kind() == reflect.Invalid {
		return "" // 在这里返回
	}
	token := header.FieldByName("Token")
	if token.Kind() == reflect.Invalid {
		return ""
	}
	return token.String()
}
英文:

Call Kind() to check

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

得分: 0

只是在这里发布(作为一个额外的潜在解决问题)

> 在我的情况下,我有一个空值,经过 .Elem() 后,结果为 0 值。

这是我的场景(当然是简化的)

var myTime *time.Time
if v := reflect.ValueOf(myTime); v.Kind() == reflect.Ptr {
    if v.IsNil() {
	    fmt.Println("它是空值")
	} else {
	    // 做其他事情
	}
}

没有 "if" 部分,我调用了 v.Elem().Interface(),其中 Interface() 是在 "零" 值上调用的。现在的 IsNil() 防止了这种情况,一切在我的端上都正常工作

希望对你有所帮助 恐慌:反射:在零值上调用反射.Value.Call。

Max

英文:

Just posting here (as an additional potential solving issue)

> In my case, I had a nil value which was, after a .Elem(), resulting as a 0 value..

here is my scenario (simplified of course)

var myTime *time.Time
if v := reflect.ValueOf(myTime); v.Kind() == reflect.Ptr {
    if v.IsNil() {
	    fmt.Println("it is nil")
	} else {
	    // do anything else
	}
}

Without the "if" part, I was calling v.Elem().Interface() where Interface() was called on "zero" value. The IsNil() now prevents this and everything is working fine on my end

Hope it helps 恐慌:反射:在零值上调用反射.Value.Call。

Max

huangapple
  • 本文由 发表于 2017年2月2日 13:54:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/41995110.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定