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

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

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:&quot;id,omitempty&quot;`
	NodeID    *string    `json:&quot;node_id,omitempty&quot;`
	Body      *string    `json:&quot;body,omitempty&quot;`
	User      *User      `json:&quot;user,omitempty&quot;`
	Reactions *Reactions `json:&quot;reactions,omitempty&quot;`
	CreatedAt *time.Time `json:&quot;created_at,omitempty&quot;`
	UpdatedAt *time.Time `json:&quot;updated_at,omitempty&quot;`
	// AuthorAssociation is the comment author&#39;s relationship to the issue&#39;s repository.
	// 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;.
	AuthorAssociation *string `json:&quot;author_association,omitempty&quot;`
	URL               *string `json:&quot;url,omitempty&quot;`
	HTMLURL           *string `json:&quot;html_url,omitempty&quot;`
	IssueURL          *string `json:&quot;issue_url,omitempty&quot;`
}

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(&quot;I did not want something of type %s&quot;, 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.

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:

确定