英文:
Variable type of input parameter in function
问题
我创建了一个函数,用于获取用户在Pull Request上的最后一条评论。我正在使用"github.com/google/go-github/github"包。我想要将其用于[]*github.IssueComment和[]*github.PullRequestComment类型。有没有一种方法可以使输入参数的类型是可变的,这样我就不需要在函数定义中指定它,函数可以使用任一类型进行调用?
func getLastUserInteractionPR(comments_array *github.IssueComment OR *github.PullRequestComment)(*github.IssueComment OR *github.PullRequestComment) {
}
使用泛型的方式:
func getLastUserInteractionPR(comments_array any)(any) {
}
这是一种紧急解决方案,因为我正在使用Go 1.14编写整个项目,而这个功能在Go 1.18中才可用。
当我尝试使用空接口{}作为输入类型时:
func getLastUserInteractionPRIssue(comments_array interface{})(*github.IssueComment) {
comments_array []*github.IssueComment(comments_array); err {
fmt.Println("success")
} else {
fmt.Println("failure")
}
}
英文:
I created a function that gets the last comment of a user on Pull Request. I am using "github.com/google/go-github/github" package. I would like to use it for []*github.IssueComment and []*github.PullRequestComment type. Is there a way for the type of the input parameter to be variable so I dont have to specify it in the function definition and function can be called using either of the type?
func getLastUserInteractionPR(comments_array *github.IssueComment OR *github.PullRequestComment)(*github.IssueComment OR *github.PullRequestComment) {
}
Using of generics:
func getLastUserInteractionPR(comments_array any)(any) {
}
is a emergency solution, since the whole project I am working on is written in Go 1.14 and this funcionality is available from GO 1.18
When I try to use empty interface{} as input type:
func getLastUserInteractionPRIssue(comments_array interface{})(*github.IssueComment) {
comments_array []*github.IssueComment(comments_array); err {
fmt.Println("success")
} else {
fmt.Println("failure")
}
}
</details>
# 答案1
**得分**: 1
你关心例如`IssueComment`的内部结构吗?
```go
type IssueComment struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
// AuthorAssociation是评论作者与问题存储库的关系。
// 可能的值有"COLLABORATOR"、"CONTRIBUTOR"、"FIRST_TIMER"、"FIRST_TIME_CONTRIBUTOR"、"MEMBER"、"OWNER"或"NONE"。
AuthorAssociation *string `json:"author_association,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
IssueURL *string `json:"issue_url,omitempty"`
}
也就是说,你关心从中提取一些特定字段吗?PullRequestComment
是一个更大的结构体(它有更多的字段),你关心从中提取一些字段吗?
还是你只想要每个结构体的字符串表示?你要做的事情很大程度上取决于你想要对传递的值做什么。
如果你只想要每个结构体的string
表示,你可以使用极端的(而且,老实说,不太安全 - 我不推荐这样做)示例,让你的函数接受一个fmt.Stringer
对象的切片:
func DoStuffWithStringifiedComments(cs []fmt.Stringer) {
// IssueComment和PullRequestComment都提供了String()方法,因此实现了fmt.Stringer
for _, comment := range cs {
DoSomethingWith(comment.String())
}
}
现在你的切片可以包含任何类型的对象,而不会出错。缺点是:它也可以包含其他你不想要的类型。所以你需要添加一个类型断言检查:
switch t := comment.(type) {
case github.IssueComment:
// 做一些好的事情
case github.PullRequestComment:
// 做其他好的事情
default:
// 对t的值进行报错和警告
}
如果有你想要提取的字段,你需要结合一个接受类似[]interface{}
的函数(将其作为空接口的切片,而不是作为空接口代替切片),迭代它并对切片的每个元素进行类型检查,只要元素是你期望的类型,就提取出有意义的字段:
func DoStuff(comments []interface{}) error {
for _, c := range comments {
if ic, ok := c.(*github.IssueComment); ok { // 如果你的切片包含的是值而不是引用,请删除解引用
// 提取字段并执行IssueComment特定的操作
ProcessIssueComment(ic)
} else if prc, ok := c.(*github.PullRequestComment); ok {
// 执行PRComment特定的操作
ProcessPullRequestComment(prc)
} else {
return fmt.Errorf("我不想要类型为%s的东西", t)
}
}
return nil
}
另外:游说项目所有者(如果不是你)升级到当前版本的Go。1.14只在2020年发布,但在Go发布中已经是一个漫长的时间了。
英文:
Do you care about the internal structure of e.g. an IssueComment
?
type IssueComment struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
// AuthorAssociation is the comment author's relationship to the issue's repository.
// Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE".
AuthorAssociation *string `json:"author_association,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
IssueURL *string `json:"issue_url,omitempty"`
}
As in, do you care about extracting some specific field from that? PullRequestComment
is a larger struct (it has more fields), do you care about extracting some field from that?
Or do you just want a string representation of each? What you do very much depends on what you want to do with the passed value(s).
If you just want a string
representation of each, you could use the extreme (and, honestly, not very safe - I don't recommend this) example of having your function accept a slice of fmt.Stringer
objects:
func DoStuffWithStringifiedComments(cs []fmt.Stringer) {
// Both IssueComment and PullRequestComment provide String()
// methods and therefore implement fmt.Stringer
for _, comment := range cs {
DoSomethingWith(comment.String())
}
}
Your slice can now include objects of either type and nothing will blow up. Downside: it can also include a couple of bajillion other types, none of which you want. So you'd need to add a type assertion check:
switch t := comment.(type) {
case github.IssueComment:
// Do good stuff
case github.PullRequestComment:
// Do other good stuff
default:
// Yell and scream about the value of t
}
If there are fields you want to extract, you're going to have to combine a function taking something like []interface{}
(make it a slice of empty interfaces, not an empty interface standing in for the slice), iterate over it and do a type check on each element of the slice, and pull out whatever fields are meaningful as long as the element is of a type you expect:
func DoStuff(comments []interface{}) error {
for _, c : = range comments {
if ic, ok := c.(*github.IssueComment); ok { // Remove the deref if your slice contains values, not references
// Pull out fields and do IssueComment-specific things
ProcessIssueComment(ic)
} else if prc, ok := c.(*github.PullRequestComment); ok {
// Do PRComment-specific things
ProcessPullRequestComment(prc)
} else {
return(fmt.Errorf("I did not want something of type %s", t))
}
}
return nil
}
Also: lobby the project owner (if that isn't you) to move to a current version of go. 1.14 only came out in 2020, but that's an eternity in go releases.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论