Go: reflect: 调用时输入参数过少

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

Go: reflect : Call with too few input arguments

问题

我遇到了使用反射库的问题。我决定使用它是因为有很多推荐,但我只是在学习Go语言,有些部分并不容易。

我有以下代码片段:

func countDataByName(sourceName string, statData interface{}, filters Filter, chartName string) []ChartElement {
    ...
    // 步骤1 - 过滤
    filteredData := reflect.ValueOf(statData).MethodByName("FilterData").Call([]reflect.Value{})

    // 步骤2 - 聚类
    // clusterData := reflect.ValueOf(filteredData).MethodByName("clusterData").Call([]reflect.Value{})

    // 步骤3 - 计数
    // countedData := reflect.ValueOf(clusterData).MethodByName(chartName).Call([]reflect.Value{})

    fmt.Println("不会打印到任何地方", filteredData)

    ...
    return filterData
}

如果我像这样执行该方法,我会得到错误:reflect: Call with too few input arguments。但是,如果我将reflect.ValueOf(statData)更改为reflect.ValueOf(&statData),那么错误就是reflect: call of reflect.Value.Call on zero Value

statData具有两种类型之一,并且对于这些类型,我有结构体和方法,例如:

type NoaggModel struct {
    Date             string
    Hour             int
    Id_user          int
    Id_line          int
    Id_region        int
    Id_tree_devision int
    N_inb            int
    N_inb_d          int
    T_ring           int
    T_inb            int
    T_inb_d          int
    T_hold           int
    T_acw            int
    T_acw_d          int
    T_wait           int
}

func (ng *NoaggModel) FilterData(data NoaggModel) {
    fmt.Println("FilterData")
    fmt.Println("data: ", data)
}

这里的Println也不起作用。代码在上面发生了恐慌,并且方法没有被触发。我在哪里犯了错误?

更新1:

发现如果我在要调用的函数中删除参数data,那么它就会正常调用。但是!我有statData作为结构体的一行,所以类型是NoaggModel。在方法FilterData中,我将这一行作为ng。但是我需要将其更改为[]NoaggModel。在这种情况下如何调用reflect,以及如何将参数传递给过滤函数?

更新2:
我修改了一些部分:

func (ng *NoaggModel) FilterData(filter interface{}, data NoaggModel) {
    fmt.Println("data: ", ng)
}

在这里,如果它在revel控制器中设置,并且方法在模型中,那么如何将正确的类型传递给filter?或者我应该在每个模型中设置类型并在控制器中调用它?

在控制器中,我写了:

// 步骤1 - 过滤
in := make([]reflect.Value, 2)

in[0] = reflect.ValueOf(filters)
in[1] = reflect.ValueOf(statData)

filteredData := reflect.ValueOf(statData).MethodByName("FilterData").Call(in)

StatDataNoaggModel类型的一行,但我得到了错误:

reflect: Call using *models.NoaggModel as type models.NoaggModel

类型也是通过上面的reflect设置的,如下所示:

...
var sourceTypes = map[string]reflect.Type{
    "noagg":  reflect.TypeOf(models.NoaggModel{}),
    "oracle": reflect.TypeOf(models.OracleModel{}),
}
deserializedData = reflect.New(sourceTypes[sourceName]).Interface()
...
// deserializedData 变成了 statData
英文:

I've stucked with problems of using reflect library. I descided to use it because of many recomendations, but i'm just learning go and some parts are not really easy..

I've got this part of code :

 func countDataByName(sourceName string, statData interface{}, filters Filter, chartName string) []ChartElement {
            ...
   	//step 1 - filter
	filteredData := reflect.ValueOf(statData).MethodByName("FilterData").Call([]reflect.Value{})

	//step 2 - cluster
    //	clusterData := reflect.ValueOf(filteredData).MethodByName("clusterData").Call([]reflect.Value{})

	//step 3 - count
    //	countedData := reflect.ValueOf(clusterData).MethodByName(chartName).Call([]reflect.Value{})
    
    fmt.Println("Never prints to anywhere", filteredData)
            
            ...
     return filterData
 }

If I execute the method like this, I get error : reflect: Call with too few input arguments. But if I change reflect.ValueOf(statData) on reflect.ValueOf(&statData) than error is reflect: call of reflect.Value.Call on zero Value

statData comes with one of 2 types, and fore this types I have structs and methods, like this :

type NoaggModel struct {
	Date             string
	Hour             int
	Id_user          int
	Id_line          int
	Id_region        int
	Id_tree_devision int
	N_inb            int
	N_inb_d          int
	T_ring           int
	T_inb            int
	T_inb_d          int
	T_hold           int
	T_acw            int
	T_acw_d          int
	T_wait           int
}

func (ng *NoaggModel) FilterData( data NoaggModel) {
	fmt.Println("FilterData")
	fmt.Println("data : ", data)
}

this Println also not works. Code panics above , and method was not triggered. Where is my mistake here?

Upd 1:

Found that if I remove param data in functioin that I want to call, than it calls nicely. But!
I have statData as 1 row, of structs, so type is NoaggModel. And in the method FilterData I get this 1 row as ng. But I need to change it to the []NoaggModel. How to call reflect in this case and how to pass parameter to the filter function ?

Upd 2:
I modified few parts :

func (ng *NoaggModel) FilterData(filter interface{}, data NoaggModel) {
	fmt.Println("data : ",ng)
}

In here, how to pass correct type to filter, if it is set up in revel controller, and method is in model. Or should I set the type in each model and call it in controller?

And in controller I wrote :

//step 1 - filter
	in := make([]reflect.Value, 2)

	in[0] = reflect.ValueOf(filters)
	in[1] = reflect.ValueOf(statData)

	filteredData := reflect.ValueOf(statData).MethodByName("FilterData").Call(in)

StatData is a row of type NoaggModel, but I get the error :

 reflect: Call using *models.NoaggModel as type models.NoaggModel 

The type was set also by reflect in code above, like this :

    ...
    var sourceTypes = map[string]reflect.Type{
		"noagg": reflect.TypeOf(models.NoaggModel{}),
		"oracle": reflect.TypeOf(models.OracleModel{}),
	}
    deserializedData = reflect.New(sourceTypes[sourceName]).Interface()
    ...
    // deserialised becomes statData

答案1

得分: 2

反射并不容易,如果可能的话应该避免使用。

我承认我曾建议使用reflect根据映射动态创建类型的实例,当你不知道要处理哪些类型时,这确实非常有用。但在你的情况下,你应该考虑使用接口。

虽然我不太清楚你想要实现什么,但我建议首先创建一个所有模型都需要实现的接口(根据你的需求进行修改):

type Model interface {
    FilterData(interface{})
}

然后,NoaggModelOracleModel可以通过定义类似以下方法来实现上述接口:

func (ng *NoaggModel) FilterData(filter interface{}) {
    fmt.Printf("data: %#v, filter: %#v\n", ng, filter)
}

接下来,将deserializedData(和statData)的类型从interface{}改为接口类型Model。由于你只有两种类型,可以通过使用switch语句来避免使用反射:

...
var deserializedData Model

switch sourceName {
case "noagg":
    deserializedData = new(models.NoaggModel)
case "oracle":
    deserializedData = new(models.OracleModel)
}
...
// 将值编组到现在保存所需类型实例的deserializedData中
...
deserializedData.FilterData("用你的过滤器替换这个字符串")

这样就完成了,而无需导入reflect

英文:

Reflection is not easy. And should be avoided if possible.

I admit that I did recommend using reflect to dynamically create instances of types based on a map, which is really useful when you don't know which types you might have to handle. But in your case you should consider using interfaces.

While I don't really know what you want to achieve, I would suggest starting by creating an interface that all your Models need to implement (modify it to fit your needs):

type Model interface {
    FilterData(interface{})
}

NoaggModel and OracleModel would then implement the above interface by defining similar methods like this:

func (ng *NoaggModel) FilterData(filter interface{}) {
    fmt.Printf("data: %#v, filter: %#v\n", ng, filter)
}

Then, change deserializedData (and statData) to be of the interface type Model instead of interface{}. And since you only have two types, you can avoid using reflect by having a switch instead:

...
var deserializedData Model

switch sourceName {
case "noagg":
    deserializedData = new(models.NoaggModel)
case "oracle":
    deserializedData = new(models.OracleModel)
}
...
// Marshal the values into deserializedData which now holds an instance of the desired type
...
deserializedData.FilterData("Replace this string with your filter")

And it is done without having to import reflect!

huangapple
  • 本文由 发表于 2016年2月16日 17:53:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/35429132.html
匿名

发表评论

匿名网友

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

确定