string from stream in go for multiple object types

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

string from stream in go for multiple object types

问题

我习惯使用Java,并在Google Go中进行初步设置。我有一个包含子对象等的对象树。这个树会递归地转储到io.Writer中。输出可能非常庞大,所以我不想为每个对象创建一个字符串,并在内存中连接结果。

为了调试目的,我想要在这个树的部分中使用fmt.Printf。因此,我想要在每个对象上创建一个通用的String()函数,该函数调用ToStream函数,并将结果作为字符串返回。在Java中,这很容易:在基类上创建该方法。在GO中,我该如何做到这一点,而不需要为每种类型的对象创建自定义的String方法。

请参考以下代码,特别是标记为ERROR的行:

package main

import (
	"io"
	"fmt"
	"bytes"
)

//Base是用于批量输出的接口
type Base interface {
	ToStream(io.Writer)
}

//Impl1具有Base接口
type Impl1 struct{
	stuff int
}

func (Impl1) ToStream(w io.Writer) {
	fmt.Fprintf(w, "A lot of stuff")
}

//Impl2具有Base接口
type Impl2 struct{
	otherstuff int
}

func (Impl2) ToStream(w io.Writer) {
	fmt.Fprintf(w, "A lot of other stuff")
}

//我想要将任何基类转换为用于调试输出的字符串
//这应该通过ToStream方法实现

func (v Base) String() string {//错误在这里:无效的接收器类型Base(Base是一个接口类型)
//func (v Impl1) String() string {//这样可以工作,但需要为每个struct Impl1、Impl2等重新实现
	var buffer bytes.Buffer
	v.ToStream(&buffer)
	return string(buffer.Bytes())
}

func main(){
	aBase:= new(Impl1)
	fmt.Printf("%s\n",aBase)
}
英文:

I'm used to Java, and setting first steps in google go. I have a tree of objects with child objects etc... This tree is recursively dumped to an io.Writer. Output might be huge, so I don't want to create a string for each object, and concatenate the result in memory..

For debugging purposes, i want to fmt.Printf parts of this tree. Thus, I want to create a generic String() function on each object in which calls the ToStream function, returning the result as a string. In Java, this is easy: create the method on the base class. How do I do this in GO, without creating a custom String method for each kind of object.

See the code for what I want, specifically the line marked ERROR

package main

import (
"io"
"fmt"
"bytes"
)

//Base is an interface for bulk output
type Base interface {
    ToStream(io.Writer)
}

//Impl1 has interface Base
type Impl1 struct{
    stuff int
}

func (Impl1) ToStream(w io.Writer) {
    fmt.Fprintf(w, "A lot of stuff")
}

//Impl2 has interface Base
type Impl2 struct{
    otherstuff int
}

func (Impl2) ToStream(w io.Writer) {
    fmt.Fprintf(w, "A lot of other stuff")
}

//I want to convert any base to a sting for debug output
//This should happen by the ToStream method

func (v Base) String() string {//ERROR here: Invalid receiver type Base (Base is an interface type)
//func (v Impl1) String() string {//This works, but requires re-implementation for every struct Impl1,Impl2,...
    var buffer bytes.Buffer
    v.ToStream(&buffer)
    return string(buffer.Bytes())
}

func main(){
    aBase:= new(Impl1)
    fmt.Printf("%s\n",aBase)
}

答案1

得分: 3

似乎你在这里被Java的思维所限制了 string from stream in go for multiple object types

虽然Java只有方法,但Go确实有函数。当然,你不能在接口上定义方法,但你可以创建一个接受Base类型参数并执行操作的普通函数:

func Base2String(b Base) string {
    var buffer bytes.Buffer
    b.ToStream(&buffer)
    return string(buffer.Bytes())
}

现在,如果你将Base重命名为符合Go语言风格的名称(记住,Go语言中没有类型层次结构),你就会得到一些漂亮的代码。

英文:

Seems like Java thinking blocked you here string from stream in go for multiple object types

While Java has methods only Go does have functions. And of course you cannot have methods on an interface but you can make a plain function taking a Base and doing stuff:

func Base2String(b Base) string {
    var buffer bytes.Buffer
    b.ToStream(&buffer)
    return string(buffer.Bytes())
}

Now if you rename Base to something Go-ish (remember there is no type hierarchy in Go) you have some nice code.

答案2

得分: 1

你可以使用一个包装器来添加必要的String()函数。以下是一种方法:

type StreamerToStringer struct {
    Base
}

func (s StreamerToStringer) String() string {
    var buffer bytes.Buffer
    s.Base.ToStream(&buffer)
    return string(buffer.Bytes())
}

通过这种方式,你可以扩展任何Base实例,使其具有String()方法。

func main() {
    aBase1 := StreamerToStringer{new(Impl1)}
    aBase2 := StreamerToStringer{new(Impl2)}
    fmt.Printf("%s\n", aBase1)
    fmt.Printf("%s\n", aBase2)
    
    // 这些包装后的值仍然支持ToStream()方法。
    var buffer bytes.Buffer
    aBase1.ToStream(&buffer)
    fmt.Println(buffer.Bytes())
}

请注意,这只是一个示例代码,Impl1Impl2是示意的实现类型。你需要根据实际情况进行适当的修改。

英文:

You can wrap around a Base to add the necessary String() function. Here is one approach:

type StreamerToStringer struct {
	Base
}

func (s StreamerToStringer) String() string {
	var buffer bytes.Buffer
	s.Base.ToStream(&buffer)
	return string(buffer.Bytes())
}

With this, you can augment any Base instance so it has a String() method.

func main() {
	aBase1 := StreamerToStringer{new(Impl1)}
	aBase2 := StreamerToStringer{new(Impl2)}
	fmt.Printf("%s\n", aBase1)
	fmt.Printf("%s\n", aBase2)
	
	// These wrapped values still support ToStream().
	var buffer bytes.Buffer
	aBase1.ToStream(&buffer)
	fmt.Println(buffer.Bytes())
}

答案3

得分: 0

首先,你可以尝试使用以下代码来打印信息:

fmt.Printf("%+v\n", yourProject)

查看打印出的信息是否足够开始使用。fmt中提到:

当打印结构体时,加号标志(%+v)会添加字段名。

如果这还不够,你可以使用反射,就像我在"Golang - How to print struct variables in console?"中提到的那样。

或者你可以查看项目davecgh/go-spew(在"Go-spew: A Journey into Dumping Go Data Structures"中提到)。

Go-spew 实现了一个用于调试的深度打印机,用于打印 Go 数据结构。

spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)

这将打印类似以下的内容:

(main.Foo) {
 unexportedField: (*main.Bar)(0xf84002e210)({
  flag: (main.Flag) flagTwo,
  data: (uintptr) <nil>
 }),
 ExportedField: (map[interface {}]interface {}) {
  (string) "one": (bool) true
 }
}
([]uint8) {
 00000000  11 12 13 14 15 16 17 18  19 1a 1b 1c 1d 1e 1f 20  |............... |
 00000010  21 22 23 24 25 26 27 28  29 2a 2b 2c 2d 2e 2f 30  |!"#$%&amp;&#39;()*+,-./0|
 00000020  31 32                                             |12|
}
英文:

It is easier to first call:

fmt.Printf(&quot;%+v\n&quot;, yourProject)

See if the information printed are enough for a start: the fmt package mentions

> when printing structs, the plus flag (%+v) adds field names

If that is not enough, then you would have to use reflection, as I mentioned in "Golang - How to print struct variables in console?".

Or you can have a look at the project davecgh/go-spew (mentioned in "Go-spew: A Journey into Dumping Go Data Structures")

> Go-spew implements a deep pretty printer for Go data structures to aid in debugging

spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)

That would print something like:

(main.Foo) {
 unexportedField: (*main.Bar)(0xf84002e210)({
  flag: (main.Flag) flagTwo,
  data: (uintptr) &lt;nil&gt;
 }),
 ExportedField: (map[interface {}]interface {}) {
  (string) &quot;one&quot;: (bool) true
 }
}
([]uint8) {
 00000000  11 12 13 14 15 16 17 18  19 1a 1b 1c 1d 1e 1f 20  |............... |
 00000010  21 22 23 24 25 26 27 28  29 2a 2b 2c 2d 2e 2f 30  |!&quot;#$%&amp;&#39;()*+,-./0|
 00000020  31 32                                             |12|
}

huangapple
  • 本文由 发表于 2014年10月22日 17:15:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/26504089.html
匿名

发表评论

匿名网友

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

确定