执行:反射:使用很少的输入参数进行调用

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

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
", 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!