英文:
Golang: implicit struct matching
问题
考虑以下代码:
type Rectangle struct {
Width, Height, Area int
}
type Square struct {
Side, Area int
}
type Geometry struct {
Area int
}
func SumGeometries(geometries ...Geometry) (sum int) {
for _, g := range geometries {
sum += g.Area
}
return
}
func TestSumGeometries(t *testing.T) {
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
got := SumGeometries(rect, square) // 无法将rect(Rectangle类型的变量)作为Geometry类型的参数传递给MyFunc,编译器报错
want := 45
if got != want {
t.Error("fail!")
}
}
我希望MyFunc可以接受包含Apple的任何结构体,而不仅仅是特定的BStruct。在Go语言中是否可以实现这一点?
目前我能找到的唯一方法是:
type Rectangle struct {
Width, Height, Area int
}
func (r *Rectangle) GetArea() int {
return r.Area
}
type Square struct {
Side, Area int
}
func (s *Square) GetArea() int {
return s.Area
}
type Areaer interface {
GetArea() int
}
func SumGeometries(geometries ...Areaer) (sum int) {
for _, s := range geometries {
sum += s.GetArea()
}
return
}
func TestArgs(t *testing.T) {
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
got := SumGeometries(&rect, &square) // 无法将rect(Rectangle类型的变量)作为Geometry类型的参数传递给MyFunc,编译器报错
want := 45
if got != want {
t.Error("fail!")
}
}
但这种方式可能不太符合惯用写法:当我已经满足于消费者直接访问数据时,是否需要向结构体中添加一个不必要的方法呢?
英文:
Consider this code:
type Rectangle struct {
Width, Height, Area int
}
type Square struct {
Side, Area int
}
type Geometry struct {
Area int
}
func SumGeometries(geometries ...Geometry) (sum int) {
for _, g := range geometries {
sum += g.Area
}
return
}
func TestSumGeometries(t *testing.T) {
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
got := SumGeometries(rect, square) // cannot use rect (variable of type Rectangle) as Geometry value in argument to MyFunc compilerIncompatibleAssign
want := 45
if got != want {
t.Error("fail!")
}
}
I want MyFunc to take whatever struct that contains Apple, not just BStruct in specific.
Is this achievable in Go?
The only way I can find ATM is the following:
type Rectangle struct {
Width, Height, Area int
}
func (r *Rectangle) GetArea() int {
return r.Area
}
type Square struct {
Side, Area int
}
func (s *Square) GetArea() int {
return s.Area
}
type Areaer interface {
GetArea() int
}
func SumGeometries(geometries ...Areaer) (sum int) {
for _, s := range geometries {
sum += s.GetArea()
}
return
}
func TestArgs(t *testing.T) {
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
got := SumGeometries(&rect, &square) // cannot use rect (variable of type Rectangle) as Geometry value in argument to MyFunc compilerIncompatibleAssign
want := 45
if got != want {
t.Error("fail!")
}
}
It feels perhaps not idiomatic: would I want to pollute my struct with an unnecessary method when I'd be already happy with consumers accessing the data directly?
答案1
得分: 2
将方法添加到类型中并不会“污染”它。
但是有一种方法可以实现你想要的功能而不重复定义。定义一个Area
类型,其中包含具有GetArea()
方法的公共字段(在这里是Area
):
type Area struct {
Value int
}
func (a Area) GetArea() int {
return a.Value
}
然后将其嵌入到其他类型中:
type Rectangle struct {
Width, Height int
Area
}
type Square struct {
Side int
Area
}
这样GetArea()
方法就会被提升,并且Rectangle
和Square
将自动实现Areaer
接口。测试一下:
rect := Rectangle{5, 4, Area{20}}
square := Square{5, Area{25}}
got := SumGeometries(rect, square)
want := 45
if got != want {
fmt.Println("fail!")
}
没有输出(没有错误)。在Go Playground上试一试。
请注意,如果Area
只包含一个字段,甚至可以省略包装结构体,直接使用int
作为底层类型:
type Area int
func (a Area) GetArea() int {
return int(a)
}
然后使用起来更简单:
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
在Go Playground上试一试。
英文:
Adding methods to types it not "polluting".
But there's a way to achieve what you want without repetition. Define an Area
type holding the common (here Area
) field with GetArea()
method:
type Area struct {
Value int
}
func (a Area) GetArea() int {
return a.Value
}
And embed this in other types:
type Rectangle struct {
Width, Height int
Area
}
type Square struct {
Side int
Area
}
This way the GetArea()
method gets promoted, and Rectangle
and Square
will automatically implement Areaer
. Testing it:
rect := Rectangle{5, 4, Area{20}}
square := Square{5, Area{25}}
got := SumGeometries(rect, square)
want := 45
if got != want {
fmt.Println("fail!")
}
Outputs nothing (no error). Try it on the Go Playground.
Note that if Area
contains a single field only, you can even omit the wrapper struct and use int
as the underlying type directly:
type Area int
func (a Area) GetArea() int {
return int(a)
}
Then using it is simpler:
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
Try this one on the Go Playground.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论