如何将不同的结构传递给函数?

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

How pass different structures to function?

问题

我有几种不同的结构。

这里展示两个:

type AdsResponse struct {
	Body struct {
		Docs []struct {
			ID        int  `json:"ID"`
			// 其他字段

		} `json:"docs"`
	} `json:"response"`
	Header `json:"responseHeader"`
}

type OtherResponse struct {
	Body struct {
		Docs []struct {
			ID    int     `json:"ID"`
			// 其他字段
		} `json:"docs"`
	} `json:"response"`
	Header `json:"responseHeader"`
}

但我不知道如何使这个方法既接受参数,又返回参数。

func Get(url string, response Response) (Response, bool) {

	res, err := goreq.Request{
		Uri:         url,
	}.Do()

	// 几个验证步骤

	res.Body.FromJsonTo(&response)

	return response, true
}

使用方法如下:

var struct1 AdsResponse
var struct2 OtherResponse

Get("someURL", struct1)
Get("someURL", struct2)

有什么解决方法吗?

英文:

I have several different structures.

Here show two:

type AdsResponse struct {
	Body struct {
		Docs []struct {
			ID        int  `json:"ID"`
			// others

		} `json:"docs"`
	} `json:"response"`
	Header `json:"responseHeader"`
}

type OtherResponse struct {
	Body struct {
		Docs []struct {
			ID    int     `json:"ID"`
			// others
		} `json:"docs"`
	} `json:"response"`
	Header `json:"responseHeader"`
}

but i don't know how i can do for this method accepts and return both.

func Get(url string, response Response) (Response, bool) {

	res, err := goreq.Request{
		Uri:         url,
	}.Do()

	// several validations

	res.Body.FromJsonTo(&response)

	return response, true
}

And use like this:

var struct1 AdsResponse
var struct2 OtherResponse

Get("someURL", struct1)
Get("someURL", struct2)

There are any form?

答案1

得分: 1

你的代码示例有些令人困惑,因为两个结构体看起来是相同的。我假设它们在"others"字段上有所不同。

首先,我通常建议在这种JSON反序列化的情况下创建一个包装器。直接在JSON结构上操作是脆弱的。大部分程序不应该意识到数据是以JSON形式传输的。因此,你可以创建一个包含"AdsResponse"的"Ads"结构体的包装器,或者只复制它关心的部分。这样做也会使下面的一些实现变得稍微容易一些,而且不那么脆弱。

最常见的解决方案可能是创建一个接口:

type Response interface {
    ID() int
}

你让"Ads"和"Others"都符合"Response"接口。然后你可以返回"Response"类型。如果需要的话,你可以在后面使用类型断言来确定你有哪个类型的数据,并进行其他操作。

switch response := response.(type) {
case Ads:
    ...
case Other:
    ...
}
英文:

Your code example is somewhat confusing since both structs appear to be identical. I'll assume that they differ somewhere in "others".

First, I generally recommend creating a wrapper around these kinds of JSON deserializations. Working directly on the JSON structure is fragile. Most of your program should not be aware of the fact that the data comes down in JSON. So for instance, you can wrap this in an Ads struct that contains an AdsResponse, or just copies the pieces it cares about out of it. Doing that will also make some of the below slightly easier to implement and less fragile.

The most common solution is probably to create an interface:

type Response interface {
    ID() int
}

You make both Ads and Others conform to Response. Then you can return Response. If necessary, you can type-switch later to figure out which one you have and unload other data.

switch response := response.(type) {
case Ads:
    ...
case Other:
    ...
}

答案2

得分: 1

我不太明白为什么你将响应作为参数和返回值。我认为你不需要返回它。你应该传递一个指向响应的指针,并用数据填充它。此外,我会返回一个错误而不是布尔值,但这是另一个话题。

无论如何,解决方案是使用interface{}(空接口)。

你很幸运,因为你正在使用的函数(FromJsonTo)接受一个空接口作为参数,所以你可以安全地将参数类型更改为interface{},然后将其传递给FromJsonTo。像这样:

func Get(url string, response interface{}) bool {

    res, err := goreq.Request{
        Uri:         url,
    }.Do()

    // 进行一些验证

    res.Body.FromJsonTo(response)

    return true
}

警告:我没有编译代码。

然后,你可以使用URL和响应结构体的指针调用这个函数,像这样:

var struct1 AdsResponse
var struct2 OtherResponse

Get("someURL", &struct1)
Get("someURL", &struct2)
英文:

I don't quite get why you have the reponse as a parameter and as a return. I think you dont need to return it. You should pass a pointer to the reponse and fill it with the data. Also, I'd return an Error instead of a boolean, but that is another topic.

Anyway, the solution is to use interface{} (empty interface).

You are lucky because the function you are using (FromJsonTo) accepts an empty interface as a parameter, so you can safely change your parameter type to interface{} and just pass it to FromJsonTo. Like this:

func Get(url string, response interface{}) bool {

    res, err := goreq.Request{
        Uri:         url,
    }.Do()

    // several validations

    res.Body.FromJsonTo(response)

    return true
}

Warning: I did not compile the code.

Then you would call this function with the url and a pointer to one of the reponse structs like this:

var struct1 AdsResponse
var struct2 OtherResponse

Get("someURL", &struct1)
Get("someURL", &struct2)

答案3

得分: 0

请注意,我将为您翻译以下内容:

在Go Playground上查看解决方案:

Go语言没有多态或其他面向对象的行为,所以当您尝试将AdsResponse或OtherResponse结构体作为Response(或任何interface{})传递时,这些值会变成Response(或其他指定的参数类型),Go无法推断出生成这些interface{}的真实类型,并正确地将json解码为这些预期的结构体类型。

这种情况在Java、C#等面向对象的语言中应该可以完美地工作。但在Go语言中,结构体和接口之间没有层次结构的泛化/特化。

您需要在您的REST执行器中进行类型断言或使用switch case,但是如果您的程序中有多个结构体,为每个结构体创建一个switch case可能不是一个合理的做法。也许很快您就会有几十个或几百个结构体。

我认为一个合理的解决方案是REST客户端传递一个lambda函数来完成最后一步操作,即创建正确的结构体目标类型并调用json解码。

正如我在上面所说的,我的示例中executeRest()的返回类型将变为interface{},但是REST客户端可以在调用executeRest()后安全地进行返回值的类型断言。

英文:

Follow the solution working at Go Playground

Go has no polymorphic or any other OO like behaviour, so, when you try to pass a AdsResponse or OtherResponse struct as an Response (or any interface{}), these values becomes an Response (or other param type specified), and is not possible to Go to infer the real type that originate these interface{} and correctly decode your json to these struct types as expected.

This kind of thing should works perfectly in OO languages, like Java, C# etc. There is no hierarchy generalization/specialization on structs/interfaces in Go.

You would need to do a type assertion in your Rest executor, or a switch case, but it seems that you need a generic REST executor, like a generic lib some thing like that. Would not reasonable create a switch case for each struct in your program. Maybe you have dozens or hundreds of structs soon.

I think that a reasonable solution is the rest client pass a lambda function to do the last step for your, that is just create a correct struct destination type and call json decode.

As i say above, the return type of executeRest() in my example will became an interface{}, but the rest client can securely do the type assertion of returned value after executeRest() call.

答案4

得分: 0

实现这一点的方法是通过Go的接口。有两种选择:

空接口

Get(url string, response interface{}) (Response, bool)

这个选项允许将任何值传递给该函数。

自定义接口

创建一个自定义接口将允许你缩小可以作为函数参数提供的类型范围。

在这种情况下,你需要创建一个所有响应结构体都需要遵守的接口。任何遵守该接口的结构体都可以作为函数参数使用。

类似这样:

type MyResponse interface {
    SomeFunction()
}

然后你的函数签名可以是这样的:

Get(url string, response MyResponse) (MyResponse, bool)

只要AdsResponseOtherResponse遵守MyResponse接口,它们就可以作为函数的参数使用。

英文:

The way to achieve this is through Go's interfaces.

Two options:

empty interface

Get(url string, response interface{}) (Response, bool)

This option allows any value to be given to this function.

custom interface

Creating a custom interface will allow you to narrow down the types that can be provided as arguments to your function.

In this case you'll have to create an interface that all your Response structs will need to abide by. Any struct really that abides by that interface will be able to be used as an argument of your function.

Something like this:

type MyResponse interface {
    SomeFunction()
} 

Then your function signature could look like

Get(url string, response MyResponse) (MyResponse, bool)

As long as AdsResponse and OtherResponse abide by the MyResponse interface, they will be allowed to be used as arguments to the function.

huangapple
  • 本文由 发表于 2015年7月9日 22:23:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/31320334.html
匿名

发表评论

匿名网友

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

确定