如何在golang中创建一个自动调用的结构体方法?

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

How to create a method of a structure in golang that is called automatically?

问题

我必须在golang中创建一个类似于两个层次继承的替代物,即在一个包中,我有一个结构(A),它被另一个包中的另一个结构(B)继承(作为匿名字段嵌入)。该结构的对象将由“main”包使用。

现在,我为“B”(BPlease)创建了一个初始化方法,该方法返回B的对象(称为B_obj)。我可以在程序开始时从“main”包中调用此初始化程序(BPlease)。

“B”的一个方法(例如,HelloB())在执行期间使用“B”的对象调用“A”的方法(例如,HelloA())。

但是我真正想要的是,为“A”创建一个类似于构造函数的方法,可以在“B”调用“A”的任何方法之前初始化其字段(最好是在“main”包中创建B_obj时)。

如何实现这一点?

我尝试为“A”也创建了一个初始化程序(APlease),并调用它(BPlease)以获取“A”的对象(A_obj)。但是我发现这个对象是无用的,因为我无法使用它来调用“B”的方法(HelloB())中的“A”的方法(HelloA())。如果有人告诉我如何使用这个对象(A_obj)将会很好。

这里有一些代码来澄清我的问题:

    package A
    type A struct { 任何内容 }
    func (A_obj *A) HelloA(){执行某些操作...}           // A()的一个方法
    func APlease() A {
          return A{初始化A字段}
        }
-------------------------------------------------------------------------------
    package B
    type B struct {
      A
      B字段
    }
    
    func BPlease() B {
      return B{
      A_obj := APlease()                     // 对我来说没用...如何利用它?
      初始化B字段}
    }

    func (B_obj *B) HelloB(){                            // B{}的一个方法
      调用B_obj.HelloA()                         // A是B结构中的匿名字段,所以这是有效的
      其他操作                       // 但是A的字段对于B_obj来说没有初始化
...}         

---------------------------------------------------
package main

import "B"
import "A"

func main(){
  B_obj := B.BPlease()         // 我想要的是,调用这个应该也为B_obj初始化A的字段,这样当HelloB()调用B_obj.HelloA()时,它会使用已经初始化的A的字段。
}

由于有很多字段,我无法将所有字段值作为参数传递给B_obj,而且一些字段值是通过调用相同结构的方法生成的。

英文:

I have to create a something like a substitute of 2 levels of inheritance in golang, i.e.,
in a package, I have a structure(A), which is inherited(embedded as an anonymous field) by another structure(B) in another package, whose object is to be utilized by the "main" package.

Now, I've created an initializer method for the "B" (BPlease) that returns an object of B (say,B_obj). I can call this initializer(BPlease) from my "main" package at the start of the program.

One of the methods of "B" (say, HelloB()), calls a method of "A"(say,HelloA()) during execution, using "B's" object.

But what I really want is, something like a constructor for "A" that can initialize its fields (preferably when B_obj was created in package "main") before "B" calls any methods of "A".

How to achieve this?

I tried creating an initializer(APlease) for "A" as well and called it (BPlease) to get an object of "A" (A_obj). But I found this object useless as I couldn't utilize it to call "A's" method (HelloA()) inside a method of "B" (HelloB()).
It would be great if someone can tell me how to utilize this object (A_obj).

Here's some code to clarify my query:

    package A
    type A struct { whatever }
    func (A_obj *A) HelloA(){performs some operation...}           // a method of A()
    func APlease() A {
          return A{initialize A fields}
        }
-------------------------------------------------------------------------------
    package B
    type B struct {
      A
      B fields
    }
    
    func BPlease() B {
      return B{
      A_obj := APlease()                     // useless to me.... how to utilise this?
      initialize B fields}
    }

    func (B_obj *B) HelloB(){                            // a method of B{}
      call B_obj.HelloA()                         // valid as A is an anon field in B struct
      some other operations                       // but A's fields are not initialized for B_obj
...}         

---------------------------------------------------
package main

import "B"
import "A"

func main(){
  B_obj := B.BPlease()         // what I want is, calling this should initialize A's fields for B_obj as well so that when HelloB() calls B_obj.HelloA(), it utilises A's field that have been initialized.
}

I cannot pass all field-values as parameters to B_obj as there are a lot of fields, and also, some field values are generated by calling a method of the same structure.

答案1

得分: 9

不管任何人对于在没有继承的情况下使用语言进行战斗的观点如何:不,没有像“getter”或“setter”之类的“魔法”方法。也许与之有关的是finalizers(终结器),但它们肯定对这种情况没有帮助。

然而,让我建议停止使用Go编写类似X语言的代码。只使用Go。Go不使用“类似”的继承,也不应该(通常)使用Go程序员。将Go视为现代化的C语言。没有太多依赖继承的C代码存在。(好吧,我知道GObject;-)

英文:

Regardless of anyone's opinion about fighting the language to have inheritance when it doesn't: No, there are no magic methods, like "getter" or "setter" of whatever. Remotely related (magic powers) are perhaps finalizers, but they're surely not going to help in this case.

However, let me suggest to stop coding language X in Go. Just use Go. Go doesn't use "class-like" inheritance, nor a Go programmer (usually) should. Think of Go like a modernized C. There's no much C code out there relying on inheritance. (OK, I know about GObject 如何在golang中创建一个自动调用的结构体方法?

答案2

得分: 3

一些元注释:“第一结构”和“第二结构”使人很难理解哪个是哪个。像A、B和C这样标记不同的事物是使数学如此强大的工具。

这是你的问题吗:
你有两种类型A和B,B嵌入A。你想确保B在A也初始化的意义上是“完全初始化”的。

原始草图:

type A struct { 任何东西 }
type B struct {
  A
  更多的东西
}

func APlease(参数为A的参数) A {
  return A{从参数设置的字段}
}

func BPlease(参数为A和B的参数) B {
  return B{
    A: APlease(A的参数),
    more: 从B的参数设置,
  }
}

应该这样做:你可以通过使用嵌入的A和B的必要参数来调用BPlease来请求正确设置的B。

英文:

Some meta-remark: "First structure" and "second structure" make it very hard to understand which one is which. Labeling the different things like A, B and C is the tool which make math so powerful.

Is this your question:
You have two type A and B, B embeds A. You want to make sure B is "fully initialized" in the sense of A is also initialized.

Raw sketch:

type A struct { whatever }
type B struct {
  A
  more stuff
}

func APlease(params for an A) A {
  return A{fields set up from params}
}

func BPlease(params forn an A and for an B) B {
  return B{
    A: APlease(stuff for A),
    more: set from params for B,
  }
}

Should do this: You can ask for a proper set up B by calling BPlease with the necessary parameters for both, the embedded A and the rest of B.

答案3

得分: 1

为了扩展Volker的答案,你应该能够调用

func BPlease() B {
  a_obj := A.APlease() // 像正常情况下初始化A的字段
  b_obj := B{} // 创建一个B,其匿名字段尚未初始化

  b_obj.A = a_obj // 可能是你想要的:将所有a的字段复制到b的字段中。
                  // 如果你依赖将a_obj的地址发送到APlease()中的某个地方,你可能运气不好。

  b_obj.field_unique_to_B = "initialized"
  return b_obj
}

现在你可以通过APlease()初始化的字段创建B对象,可以在B的对象上调用A的方法,甚至可以在B内部调用A的方法,如下所示:

func (B_obj *B) HelloB(){
  // 不能像你期望的那样调用B_obj.HelloA()
  B_obj.A.HelloA() // 这样可以。Go“提升”了匿名字段的方法和字段到B_obj中
                   // 但它们不会出现在B_obj中,而是出现在B_obj.A中
  fmt.Printf("And hello from B too; %s", B_obj.field_unique_to_B)
}

我在这里也同意Rick-777的建议,建议你遵循Go的命名约定和惯用法;NewReader比ReaderPlease更容易阅读和理解。

我编造了一个例子,如果有人想要的话,我可以放在bitbucket上。我认为当你使用真实的隐喻时,阅读起来更容易;还有一个免责声明-这不是最好的代码,但它做了一些回答你的问题的事情。

文件:car/car.go

package car

import "fmt"

type BaseCar struct {
    Doors  int // 默认情况下,4个门。SportsCar将有2个门
    Wheels int
}

func NewBaseCar() BaseCar {
    return BaseCar{Wheels: 4, Doors: 4}
}

// 这将在稍后用于显示“子类”可以调用self.BaseCar的方法
func (c *BaseCar) String() string {
    return fmt.Sprintf("BaseCar: %d doors, %d wheels", c.Doors, c.Wheels)
}

// 这将被提升而不是重新定义
func (c *BaseCar) CountDoors() int {
    return c.Doors
}

文件sportscar/sportscar.go

package sportscar

// 你可以将SportsCar视为BaseCar的子类。但是Go没有传统的继承,如果你试图将方形的C++结构强行放入圆形的Go洞中,你可能会陷入困境。

import ( "../car" ; "fmt" )

type SportsCar struct {
    car.BaseCar // 这是匿名字段
    isTopDown   bool
}

func NewSportsCar() SportsCar {
    conv := SportsCar{} // conv.Wheels == 0

    conv.BaseCar = car.NewBaseCar() // 现在conv.Wheels == conv.Doors == 4

    conv.isTopDown = false // 仅SportsCar字段
    conv.Doors = 2         // 比BaseCar少的门
    return conv
}

// SportsCar专有方法
func (s *SportsCar) ToggleTop() {
    s.isTopDown = !s.isTopDown
}

// “重载”的字符串方法注意要访问“基”String()方法,
// 你需要通过匿名字段这样做:s.BaseCar.String()
func (s *SportsCar) String() string {
    return fmt.Sprintf("Sports%s, topdown: %t", s.BaseCar.String(), s.isTopDown)
}

文件main.go

package main

import ( "./car" ; "./sportscar" ; "fmt")

type Stringer interface { // 添加了这个复杂性来展示
    String() string // 每个汽车调用自己的String()方法
}

func main() {
    boring := car.NewBaseCar()
    fancy := sportscar.NewSportsCar()

    fmt.Printf("      %s\n", Stringer(&boring))
    fmt.Printf("%s\n", Stringer(&fancy))
    fancy.ToggleTop()
    fmt.Printf("%s\n", Stringer(&fancy))

    fmt.Println("BaseCar.CountDoors()方法可以从SportsCar中调用:", fancy.CountDoors())
}
英文:

To expand on Volker's answer a bit, you should be able to call

func BPlease() B {
  a_obj := A.APlease() // initialize the fields of A like normal
  b_obj := B{} // create a B, whose anonymous fields are not initialized yet

  b_obj.A = a_obj // PERHAPS WHAT YOU WANT: copy all a's fields to b's fields.
                  // if you are relying sending a_obj's address somewhere in 
                  // APlease(), you may be out of luck.

  b_obj.field_unique_to_B = "initialized"
  return b_obj
}

Now that you can create B objects with fields initialized by APlease(), you can call A's methods on B's objects, and even call A's methods from within B like so:

func (B_obj *B) HelloB(){
  // can't call B_obj.HelloA() like you would expect
  B_obj.A.HelloA() // this works. Go "promotes" the anonymous field's methods 
                   // and fields to B_obj
                   // but they don't appear in B_obj, they appear in B_obj.A 
  fmt.Printf("And hello from B too; %s", B_obj.field_unique_to_B)
}

I will echo Rick-777 here and suggest you stick to go's naming conventions and idioms; NewReader is much easier to read and understand than ReaderPlease.

I contrived an example that I can put on bitbucket if people want. I think it's much easier to read when you are working with real metaphors; also a disclaimer - this is not the best code, but it does some things that answer your question.

file: car/car.go

package car

import "fmt"

type BaseCar struct {
	Doors  int // by default, 4 doors. SportsCar will have 2 doors
	Wheels int
}

func NewBaseCar() BaseCar {
	return BaseCar{Wheels: 4, Doors: 4}
}

// this will be used later to show that a "subclass" can call methods from self.BaseCar
func (c *BaseCar) String() string {
	return fmt.Sprintf("BaseCar: %d doors, %d wheels", c.Doors, c.Wheels)
}

// this will be promoted and not redefined
func (c *BaseCar) CountDoors() int {
	return c.Doors
}

file sportscar/sportscar.go

package sportscar

// You can think of SportsCar as a subclass of BaseCar. But go does
// not have conventional inheritence, and you can paint yourself into
// a corner if you try to force square c++ structures into round go holes.

import ( "../car" ; "fmt" )

type SportsCar struct {
	car.BaseCar // here is the anonymous field
	isTopDown   bool
}

func NewSportsCar() SportsCar {
	conv := SportsCar{} // conv.Wheels == 0

	conv.BaseCar = car.NewBaseCar() // now conv.Wheels == conv.Doors == 4

	conv.isTopDown = false // SportsCar-only field
	conv.Doors = 2         // Fewer Doors than BaseCar
	return conv
}

// SportsCar only method
func (s *SportsCar) ToggleTop() {
	s.isTopDown = !s.isTopDown
}

// "overloaded" string method note that to access the "base" String() method, 
// you need to do so through the anonymous field: s.BaseCar.String()
func (s *SportsCar) String() string {
	return fmt.Sprintf("Sports%s, topdown: %t", s.BaseCar.String(), s.isTopDown)
}

file main.go

package main

import ( "./car" ; "./sportscar" ; "fmt")

type Stringer interface { // added this complication to show
	String() string // that each car calls its own String() method
}

func main() {
	boring := car.NewBaseCar()
	fancy := sportscar.NewSportsCar()

	fmt.Printf("      %s\n", Stringer(&boring))
	fmt.Printf("%s\n", Stringer(&fancy))
	fancy.ToggleTop()
	fmt.Printf("%s\n", Stringer(&fancy))

	fmt.Println("BaseCar.CountDoors() method is callable from a SportsCar:", fancy.CountDoors())
}

答案4

得分: -1

在Go语言中,如果你正在寻找的是模拟继承的方法,有一些方式可以实现,可以参考这个博客中的“继承”部分。

英文:

There are ways to mimic inheritance in Go if this is what you are looking for, see section "Inheritance" in this blog

huangapple
  • 本文由 发表于 2013年6月3日 21:29:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/16897925.html
匿名

发表评论

匿名网友

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

确定