来自流中的字符串,用于多种对象类型

Posted

技术标签:

【中文标题】来自流中的字符串,用于多种对象类型【英文标题】:string from stream in go for multiple object types 【发布时间】:2014-12-17 16:54:22 【问题描述】:

我习惯了 Java,并在 google go 中设置了第一步。我有一棵带有子对象等的对象树……这棵树递归地转储到 io.Writer。输出可能很大,所以我不想为每个对象创建一个字符串,并将结果连接到内存中..

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

查看我想要的代码,特别是标记为 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)

【问题讨论】:

我们可以向“Base”类添加实现的断言似乎是错误的:Base 是一个接口。接口没有方法,至少在 JDK 8 之前没有,最初的提问者几乎肯定不会在谈论默认的接口方法。提问者可能试图做的是使用继承。更喜欢委托而不是继承方法。 【参考方案1】:

似乎 Java 思维在这里阻止了你 :-)

虽然 Java 有方法,但只有 Go 有函数。当然,你不能在接口上使用方法,但你可以制作一个普通函数,使用 Base 并做一些事情:

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

现在,如果您将 Base 重命名为 Go-ish(记住 Go 中没有类型层次结构),您将获得一些不错的代码。

【讨论】:

【参考方案2】:

您可以环绕 Base 以添加必要的 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 := StreamerToStringernew(Impl1)
    aBase2 := StreamerToStringernew(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())

【讨论】:

this 和 Volkers 解决方案都是很好的路径,但是这个允许我使用 fmt.Printf 而不需要包装函数调用。因此,这个解决方案对我来说似乎更可取。谢谢,伙计们!【参考方案3】:

先调用比较容易:

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

看看打印的信息是否足以开始:fmt package 提及

打印结构时,加号标志(%+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  |!"#$%&'()*+,-./0|
 00000020  31 32                                             |12|

【讨论】:

这不是我真正想要的。相应的 ToStream 函数已经成功地以正确的格式转储对象。我的问题是:如何在 ToStream 和 fmt.String 之间编写桥接方法。我的意思是:所有结构的 1 个通用,而不是实现该接口的 20 多个结构的 20 多个。现在,%+v 在其他地方肯定会派上用场,所以我无论如何都学到了一些东西! @user844382 你仍然可以从 go-spec 项目中获得灵感:你需要使用反射。

以上是关于来自流中的字符串,用于多种对象类型的主要内容,如果未能解决你的问题,请参考以下文章

Swift Codable 多种类型

从 java 8 流中的列表中过滤值

提高序列化保存到多种存档类型,并防止在使用指针加载时构造新对象

memcpy 可以用于类型双关语吗?

嵌套对象 -> 元素隐式具有“任何”类型,因为“字符串”类型的表达式不能用于索引类型

房间数据库的类型转换器不适用于具有来自另一个类的对象的类