реализовать метод аргумента generi c - PullRequest
0 голосов
/ 19 января 2020

У меня есть код в Go, который включает в себя множество методов выполнения. Каждый метод получает свою собственную структуру параметров. Я хочу, чтобы диспетчер вызывал каждый метод со своей связанной структурой.

Диспетчер получает имя метода выполнения и JSON структуры параметров. Затем он использует отражение для построения структуры и вызывает метод.

Проблема в том, что я получаю ошибку компиляции, если я не создаю методы выполнения с использованием пустого интерфейса. В приведенном ниже примере у меня есть 2 метода выполнения: api1 компилируется, но использует пустой интерфейс и явное приведение. api2 - это то, что я хочу сделать, но он завершается с ошибкой компиляции:

не может использовать api2 (тип fun c (Api2Parameters)) в качестве типа Api в присваивании

Как мне сделать компиляцию использования api2?

import (
    "encoding/json"
    "log"
    "reflect"
)

type Api func(arguments interface{})

type ApiDetails struct {
    Executor       *Api
    ParametersType reflect.Type
}

var Apis map[string]*ApiDetails

func RunApi(apiName string, data string) {
    api := Apis[apiName]
    parameters := reflect.New(api.ParametersType).Interface().(interface{})
    _ = json.Unmarshal([]byte(data), parameters)
    (*api.Executor)(parameters)
}

type Api1Parameters struct {
    Count1 int
    Id1    string
}

func api1(arguments interface{}) {
    parameters, _ := arguments.(*Api1Parameters)
    log.Printf("api1 parameters(%+v)", parameters)
}

type Api2Parameters struct {
    Count2 int
    Id2    string
}

func api2(arguments Api2Parameters) {
    log.Printf("api2 parameters(%+v)", arguments)
}

func Test() {
    // this assignment works fine
    var api_1 Api = api1
    Apis["api1"] = &ApiDetails{
        Executor:       &api_1,
        ParametersType: reflect.TypeOf(Api1Parameters{}),
    }

    // this assignment produce compile error
    var api_2 Api = api2
    Apis["api2"] = &ApiDetails{
        Executor:       &api_2,
        ParametersType: reflect.TypeOf(Api2Parameters{}),
    }

    RunApi("api1", `{"Count1":19, "Id1":"a"}`)
    RunApi("api2", `{"Count2":34, "Id2":"b"}`)
}

Ответы [ 2 ]

1 голос
/ 19 января 2020

Создайте значение, используя тип аргумента, отмените маршалинг до этого значения и вызовите функцию:

var Apis = map[string]interface{}{
    "api1": api1,
    "api2": api2,
}

func RunApi(apiName string, data string) {
    fv := reflect.ValueOf(Apis[apiName])
    ft := fv.Type()
    pin := reflect.New(ft.In(0))
    _ = json.Unmarshal([]byte(data), pin.Interface())
    fv.Call([]reflect.Value{pin.Elem()})
}

Запустите его на игровой площадке .

0 голосов
/ 19 января 2020

Если вы хотите сохранить этот тип в безопасности, вы можете немного глубже сделать sh преобразование JSON в арги:

type Api func(string)

type ApiDetails struct {
    // Don't use *Api, that's a function pointer-pointer
    Executor       Api
}

Затем используйте закрытие, чтобы сделать JSON перевод:

Apis["api1"] = &ApiDetails{
        Executor: func(data string) {
            var args ArgsForAPI1
            json.Unmarshal([]byte(string),&args)
            api1Implem(args)
        }
    }

С этим вы можете сделать:

APIs["api1"].Executor( `{"Count1":19, "Id1":"a"}`)

Вы можете изменить RunApi, чтобы сделать что-то подобное.

Секунду можно сделать это с помощью отражения:

type ApiDetails struct {
    // Executor can accept any function signature
    // Assume a function with  a single parameter
    Executor     interface{}
    ParametersType reflect.Type
}

Затем функция RunApi может использовать отражение для создания структуры и вызова Executor:

    parameters := reflect.New(api.ParametersType)
    _ = json.Unmarshal([]byte(data), parameters.Interface{})
    reflect.ValueOf(api.Executor).Call([]reflect.Value{parameters})

Это должно работать, но вы вы получите только ошибки времени выполнения, если вы все испортите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...