函数中输入参数的变量类型

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

Variable type of input parameter in function

问题

我创建了一个函数,用于获取用户在Pull Request上的最后一条评论。我正在使用"github.com/google/go-github/github"包。我想要将其用于[]*github.IssueComment和[]*github.PullRequestComment类型。有没有一种方法可以使输入参数的类型是可变的,这样我就不需要在函数定义中指定它,函数可以使用任一类型进行调用?

  1. func getLastUserInteractionPR(comments_array *github.IssueComment OR *github.PullRequestComment)(*github.IssueComment OR *github.PullRequestComment) {
  2. }

使用泛型的方式:

  1. func getLastUserInteractionPR(comments_array any)(any) {
  2. }

这是一种紧急解决方案,因为我正在使用Go 1.14编写整个项目,而这个功能在Go 1.18中才可用。

当我尝试使用空接口{}作为输入类型时:

  1. func getLastUserInteractionPRIssue(comments_array interface{})(*github.IssueComment) {
  2. comments_array []*github.IssueComment(comments_array); err {
  3. fmt.Println("success")
  4. } else {
  5. fmt.Println("failure")
  6. }
  7. }
英文:

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?

  1. func getLastUserInteractionPR(comments_array *github.IssueComment OR *github.PullRequestComment)(*github.IssueComment OR *github.PullRequestComment) {
  2. }

Using of generics:

  1. func getLastUserInteractionPR(comments_array any)(any) {
  2. }

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:

  1. func getLastUserInteractionPRIssue(comments_array interface{})(*github.IssueComment) {
  2. comments_array []*github.IssueComment(comments_array); err {
  3. fmt.Println("success")
  4. } else {
  5. fmt.Println("failure")
  6. }
  7. }
  8. </details>
  9. # 答案1
  10. **得分**: 1
  11. 你关心例如`IssueComment`的内部结构吗?
  12. ```go
  13. type IssueComment struct {
  14. ID *int64 `json:"id,omitempty"`
  15. NodeID *string `json:"node_id,omitempty"`
  16. Body *string `json:"body,omitempty"`
  17. User *User `json:"user,omitempty"`
  18. Reactions *Reactions `json:"reactions,omitempty"`
  19. CreatedAt *time.Time `json:"created_at,omitempty"`
  20. UpdatedAt *time.Time `json:"updated_at,omitempty"`
  21. // AuthorAssociation是评论作者与问题存储库的关系。
  22. // 可能的值有"COLLABORATOR"、"CONTRIBUTOR"、"FIRST_TIMER"、"FIRST_TIME_CONTRIBUTOR"、"MEMBER"、"OWNER"或"NONE"。
  23. AuthorAssociation *string `json:"author_association,omitempty"`
  24. URL *string `json:"url,omitempty"`
  25. HTMLURL *string `json:"html_url,omitempty"`
  26. IssueURL *string `json:"issue_url,omitempty"`
  27. }

也就是说,你关心从中提取一些特定字段吗?PullRequestComment是一个更大的结构体(它有更多的字段),你关心从中提取一些字段吗?

还是你只想要每个结构体的字符串表示?你要做的事情很大程度上取决于你想要对传递的值做什么。

如果你只想要每个结构体的string表示,你可以使用极端的(而且,老实说,不太安全 - 我不推荐这样做)示例,让你的函数接受一个fmt.Stringer对象的切片:

  1. func DoStuffWithStringifiedComments(cs []fmt.Stringer) {
  2. // IssueComment和PullRequestComment都提供了String()方法,因此实现了fmt.Stringer
  3. for _, comment := range cs {
  4. DoSomethingWith(comment.String())
  5. }
  6. }

现在你的切片可以包含任何类型的对象,而不会出错。缺点是:它也可以包含其他你不想要的类型。所以你需要添加一个类型断言检查:

  1. switch t := comment.(type) {
  2. case github.IssueComment:
  3. // 做一些好的事情
  4. case github.PullRequestComment:
  5. // 做其他好的事情
  6. default:
  7. // 对t的值进行报错和警告
  8. }

如果有你想要提取的字段,你需要结合一个接受类似[]interface{}的函数(将其作为空接口的切片,而不是作为空接口代替切片),迭代它并对切片的每个元素进行类型检查,只要元素是你期望的类型,就提取出有意义的字段:

  1. func DoStuff(comments []interface{}) error {
  2. for _, c := range comments {
  3. if ic, ok := c.(*github.IssueComment); ok { // 如果你的切片包含的是值而不是引用,请删除解引用
  4. // 提取字段并执行IssueComment特定的操作
  5. ProcessIssueComment(ic)
  6. } else if prc, ok := c.(*github.PullRequestComment); ok {
  7. // 执行PRComment特定的操作
  8. ProcessPullRequestComment(prc)
  9. } else {
  10. return fmt.Errorf("我不想要类型为%s的东西", t)
  11. }
  12. }
  13. return nil
  14. }

另外:游说项目所有者(如果不是你)升级到当前版本的Go。1.14只在2020年发布,但在Go发布中已经是一个漫长的时间了。

英文:

Do you care about the internal structure of e.g. an IssueComment?

  1. type IssueComment struct {
  2. ID *int64 `json:&quot;id,omitempty&quot;`
  3. NodeID *string `json:&quot;node_id,omitempty&quot;`
  4. Body *string `json:&quot;body,omitempty&quot;`
  5. User *User `json:&quot;user,omitempty&quot;`
  6. Reactions *Reactions `json:&quot;reactions,omitempty&quot;`
  7. CreatedAt *time.Time `json:&quot;created_at,omitempty&quot;`
  8. UpdatedAt *time.Time `json:&quot;updated_at,omitempty&quot;`
  9. // AuthorAssociation is the comment author&#39;s relationship to the issue&#39;s repository.
  10. // Possible values are &quot;COLLABORATOR&quot;, &quot;CONTRIBUTOR&quot;, &quot;FIRST_TIMER&quot;, &quot;FIRST_TIME_CONTRIBUTOR&quot;, &quot;MEMBER&quot;, &quot;OWNER&quot;, or &quot;NONE&quot;.
  11. AuthorAssociation *string `json:&quot;author_association,omitempty&quot;`
  12. URL *string `json:&quot;url,omitempty&quot;`
  13. HTMLURL *string `json:&quot;html_url,omitempty&quot;`
  14. IssueURL *string `json:&quot;issue_url,omitempty&quot;`
  15. }

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:

  1. func DoStuffWithStringifiedComments(cs []fmt.Stringer) {
  2. // Both IssueComment and PullRequestComment provide String()
  3. // methods and therefore implement fmt.Stringer
  4. for _, comment := range cs {
  5. DoSomethingWith(comment.String())
  6. }
  7. }

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:

  1. switch t := comment.(type) {
  2. case github.IssueComment:
  3. // Do good stuff
  4. case github.PullRequestComment:
  5. // Do other good stuff
  6. default:
  7. // Yell and scream about the value of t
  8. }

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:

  1. func DoStuff(comments []interface{}) error {
  2. for _, c : = range comments {
  3. if ic, ok := c.(*github.IssueComment); ok { // Remove the deref if your slice contains values, not references
  4. // Pull out fields and do IssueComment-specific things
  5. ProcessIssueComment(ic)
  6. } else if prc, ok := c.(*github.PullRequestComment); ok {
  7. // Do PRComment-specific things
  8. ProcessPullRequestComment(prc)
  9. } else {
  10. return(fmt.Errorf(&quot;I did not want something of type %s&quot;, t))
  11. }
  12. }
  13. return nil
  14. }

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.

huangapple
  • 本文由 发表于 2023年1月17日 00:27:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75137033.html
匿名

发表评论

匿名网友

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

确定