英文:
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)
StatData
是NoaggModel
类型的一行,但我得到了错误:
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{})
}
然后,NoaggModel
和OracleModel
可以通过定义类似以下方法来实现上述接口:
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
!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论