Go语言与数据库开发:01

  • 时间:
  • 浏览:1
  • 来源:uu快3新平台_uu快3诀窍_讨论群

带环的数据形状很少会对fmt.Sprint函数造成大问题,将会它很少尝试打印完全的数据形状。例

如,当它遇到一有三个小指针的前一天,它就说 我简单第打印指针的数值。着实,在打印含有自身的

slice或map时将会遇到困难,有后后不保证防止你这些是罕见具体情况却能只有防止额外的麻烦。

朋友儿使用 reflect.Value的 Kind 措施来替代前一天的类型 switch. 着实还是算不算穷多的类型, 有后后

它们的kinds类型却是有限的: Bool, String 和 所有数字类型的基础类型; Array 和 Struct 对应

的聚合类型; Chan, Func, Ptr, Slice, 和 Map 对应的引用类事; 接口类型; 还有表示空值的无效

类型. (空的 reflect.Value 对应 Invalid 无效类型.)

其中 TypeOf(3) 调用将值 3 作为 interface{} 类型参数传入.

strangelove := Movie{

Title: "Dr. Strangelove",

Subtitle: "How I Learned to Stop Worrying and Love the Bomb",

Year: 1964,

Color: false,

Actor: map[string]string{

"Dr. Strangelove": "Peter Sellers",

"Grp. Capt. Lionel Mandrake": "Peter Sellers",

"Pres. Merkin Muffley": "Peter Sellers",

"Gen. Buck Turgidson": "George C. Scott",

"Brig. Gen. Jack D. Ripper": "Sterling Hayden",

Maj. T.J. "King" Kong: "Slim Pickens",

},

Oscars: []string{

"Best Actor (Nomin.)",

"Best Adapted Screenplay (Nomin.)",

"Best Director (Nomin.)",

"Best Picture (Nomin.)",

},

}

一有三个小朋友儿熟悉的例子是fmt.Fprintf函数提供的字符串格式化防止逻辑,它能只有用例对任意类型

的值格式化并打印,甚至支持用户自定义的类型。让朋友儿也来尝试实现一有三个小类事功能的函

数。为了简单起见,朋友儿的函数只接收一有三个小参数,有后后返回和fmt.Sprint类事的格式化后的字

符串。朋友儿实现的函数名也叫Sprint。

reflect.Type和reflect.Value

观察下面一有三个小例子的区别:

在第三个小例子中,Display函数调用的是reflect.ValueOf(&i),它返回一有三个小指向i的指针,对应Ptr

类型。在switch的Ptr分支中,通过调用Elem来返回你这些值,返回一有三个小Value来表示i,对应

Interface类型。一有三个小间接获得的Value,就像你这些有三个小,将会代表任意类型的值,包括接口类

型。内部内部结构的display函数递归调用自身,这次它将打印接口的动态类型和值。

目前的实现,Display将会显示一有三个小带环的数据形状将会陷入死循环,类事首位项链的链表:

func Display(name string, x interface{}) {

fmt.Printf("Display %s (%T):n", name, x)

display(name, reflect.ValueOf(x))

}

package format

import (

"reflect"

"strconv"

)

// Any formats any value as a string.

func Any(value interface{}) string {

return formatAtom(reflect.ValueOf(value))

}

// formatAtom formats a value without inspecting its internal structure.

func formatAtom(v reflect.Value) string {

switch v.Kind() {

case reflect.Invalid:

return "invalid"

case reflect.Int, reflect.Int8, reflect.Int16,

reflect.Int32, reflect.Int64:

return strconv.FormatInt(v.Int(), 10)

case reflect.Uint, reflect.Uint8, reflect.Uint16,

reflect.Uint32, reflect.Uint64, reflect.Uintptr:

return strconv.FormatUint(v.Uint(), 10)

// ...floating-point and complex cases omitted for brevity...

case reflect.Bool:

return strconv.FormatBool(v.Bool())

case reflect.String:

return strconv.Quote(v.String())

case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:

return v.Type().String() + " 0x" +

strconv.FormatUint(uint64(v.Pointer()), 16)

default: // reflect.Array, reflect.Struct, reflect.Interface

return v.Type().String() + " value"

}

}

到目前为止, 朋友儿的函数将每个值视作一有三个小不可分割没人内部内部结构形状的, 有后后它叫 formatAtom.

对于聚合类型(形状体和数组)个接口就说 我打印类型的值, 对于引用类型(channels, functions,

pointers, slices, 和 maps), 它十六进制打印类型的引用地址. 着实还缺陷理想, 有后后依然是一有三个小

重大的进步, 有后后 Kind 只关心底层表示, format.Any 也支持新命名的类型. 类事:

var x int64 = 1

var d time.Duration = 1 * time.Nanosecond

fmt.Println(format.Any(x)) // "1"

fmt.Println(format.Any(d)) // "1"

fmt.Println(format.Any([]int64{x})) // "[]int64 0x8202b87b0"

fmt.Println(format.Any([]time.Duration{d})) // "[]time.Duration 0x8202b87e0"

在将会的具体情况下,你应该防止在一有三个小包中暴露和反射相关的接口。朋友儿将定义一有三个小未导出的

display函数用于递归防止工作,导出的是Display函数,它就说 我display函数简单的包装以接受

interface{}类型的参数:

朋友儿使用了switch类型分支首先来测试输入参数算不算实现了String措施,将会是励志的话 就使用该

措施。有后后继续增加类型测试分支,检查算不算每个基于string、int、bool等基础类型的动态

类型,并在帕累托图具体情况下执行相应的格式化操作。

有后后朋友儿怎么防止其它类事[]float64、map[string][]string等类型呢?朋友儿当然能只有加在更多的

测试分支,有后后有有哪些组合类型的数目基本是无穷的。还有怎么防止url.Values等命名的类型

呢?着实类型分支能只有识别出底层的基础类型是map[string][]string,有后后它不让说匹配

url.Values类型,将会它们是本身生活不同的类型,有后后switch类型分支就说 我将会含有每个类事

url.Values的类型,这会愿因对有有哪些库的循环依赖。

反射

Display递归打印

接下来,让朋友儿看看怎么改善聚合数据类型的显示。朋友儿不让说想完全一键一键复制一有三个小fmt.Sprint函

数,朋友儿就说 我像构建一有三个小用于调式用的Display函数,给定一有三个小聚合类型x,打印你这些值对应的

完全的形状,同去记录每个发现的每个元素的路径。让朋友儿从一有三个小例子后后后后刚开始 。

e, _ := eval.Parse("sqrt(A / pi)")

Display("e", e)

将会 reflect.TypeOf 返回的是一有三个小动态类型的接口值, 它老要返回具体的类型. 有后后, 下面的代

码将打印 "*os.File" 而完全都不 "io.Writer". 稍后, 朋友儿将看后 reflect.Type 是具有识别接口类型的

表达措施功能的.

var w io.Writer = os.Stdout

fmt.Println(reflect.TypeOf(w)) // "*os.File"

t := v.Type() // a reflect.Type

fmt.Println(t.String()) // "int"

逆操作是调用 reflect.ValueOf 对应的 reflect.Value.Interface 措施. 它返回一有三个小 interface{} 类型

表示 reflect.Value 对应类型的具体值:

让朋友儿针对不类事型分别讨论。

Slice和数组: 本身生活的防止逻辑是一样的。Len措施返回slice或数组值中的元素个数,Index(i)

活动索引i对应的元素,返回的也是一有三个小reflect.Value类型的值;将会索引i超出范围励志的话 将愿因

panic异常,有有哪些行为和数组或slice类型内建的len(a)和a[i]等操作类事。display针对序列中的

每个元素递归调用自身防止,朋友儿通过在递归防止时向path附加“[i]”来表示访问路径。

着实reflect.Value类型含有就说 我措施,有后后只有少数的措施对任意值完全都不 能只有安全调用的。例

如,Index措施只有对Slice、数组或字符串类型的值调用,其它类型将会调用将愿因panic异

常。

反射是一有三个小繁杂的内省技术,不应该随意使用,有后后,尽管后边有有哪些包内部内部结构完全都不 用反射技术实

现的,有后后它们当事人的API都没人公开反射相关的接口。

为什么会时需反射?

type Movie struct {

Title, Subtitle string

Year int

Color bool

Actor map[string]string

Oscars []string

Sequel *string

}

让朋友儿声明一有三个小该类型的变量,有后后看看Display函数怎么显示它:

要注意的是 reflect.Type 接口是满足 fmt.Stringer 接口的. 将会打印动态类型值对于调试和日

志是有帮助的, fmt.Printf 提供了一有三个小简短的 %T 标志参数, 内部内部结构使用 reflect.TypeOf 的结果输

出:

fmt.Printf("%Tn", 3) // "int"

一有三个小 reflect.Value 和 interface{} 都能保存任意的值. 所不同的是, 一有三个小空的接口隐藏了值对应

的表示措施和所有的公开的措施, 有后后只有朋友儿知道具体的动态类型也能使用类型断言来访问

内部内部结构的值(就像后边那样), 对于内部内部结构值并没人有点可做的事情. 相比之下, 一有三个小 Value 则有就说 我

措施来检查其内容, 无论它的具体类型是有哪些. 让朋友儿再次尝试实现朋友儿的格式化函数

format.Any.

要注意的是,形状体中未导出的成员对反射也是可见的。时需当心的是你这些例子的输出在不

同操作系统上将会是不同的,有后后随着标准库的发展也将会愿因结果不同。(这也是将有有哪些

成员定义为私有成员的愿因之一!)朋友儿深圳能只有用Display函数来显示reflect.Value,来查

看 *os.File 类型的内部内部结构表示措施。 Display("rV", reflect.ValueOf(os.Stderr)) 调用的输出如

下,当然不同环境得到的结果将会有差异:

Display rV (reflect.Value):

(*rV.typ).size = 8

(*rV.typ).hash = 8715009668

(*rV.typ).align = 8

(*rV.typ).fieldAlign = 8

(*rV.typ).kind = 22

((rV.typ).string) = "*os.File"

(((*rV.typ).uncommonType).methods[0].name) = "Chdir"

((((rV.typ).uncommonType).methods[0].mtyp).string) = "func() error"

((((rV.typ).uncommonType).methods[0].typ).string) = "func(*os.File) error"

...

在display函数中,朋友儿使用了前面定义的打印基础类型——基本类型、函数和chan等——元

素值的formatAtom函数,有后后朋友儿会使用reflect.Value的措施来递归显示聚合类型的每一有三个小成

员或元素。在递归下降过程中,path字符串,从最后后后后刚开始 传入的起始值(这里是“e”),将逐步

增长以表示怎么达到当前值(类事“e.args[0].value”)。

将会朋友儿不再模拟fmt.Sprint函数,朋友儿将直接使用fmt包来繁杂朋友儿的例子实现。

func display(path string, v reflect.Value) {

switch v.Kind() {

case reflect.Invalid:

fmt.Printf("%s = invalidn", path)

case reflect.Slice, reflect.Array:

for i := 0; i < v.Len(); i++ {

display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))

}

case reflect.Struct:

for i := 0; i < v.NumField(); i++ {

fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)

display(fieldPath, v.Field(i))

}

case reflect.Map:

for _, key := range v.MapKeys() {

display(fmt.Sprintf("%s[%s]", path,

formatAtom(key)), v.MapIndex(key))

}

case reflect.Ptr:

if v.IsNil() {

fmt.Printf("%s = niln", path)

} else {

display(fmt.Sprintf("(*%s)", path), v.Elem())

}

case reflect.Interface:

if v.IsNil() {

fmt.Printf("%s = niln", path)

} else {

fmt.Printf("%s.type = %sn", path, v.Elem().Type())

display(path+".value", v.Elem())

}

default: // basic types, channels, funcs

fmt.Printf("%s = %sn", path, formatAtom(v))

}

}

在第一有三个小例子中,Display函数将调用reflect.ValueOf(i),它返回一有三个小Int类型的值。

var i interface{} = 3

Display("i", i)

// Output:

// Display i (int):

// i = 3

Display("&i", &i)

// Output:

// Display &i (*interface {}):

// (*&i).type = int

// (*&i).value = 3

package format

import (

"reflect"

"strconv"

)

// Any formats any value as a string.

func Any(value interface{}) string {

return formatAtom(reflect.ValueOf(value))

}

// formatAtom formats a value without inspecting its internal structure.

func formatAtom(v reflect.Value) string {

switch v.Kind() {

case reflect.Invalid:

return "invalid"

case reflect.Int, reflect.Int8, reflect.Int16,

reflect.Int32, reflect.Int64:

return strconv.FormatInt(v.Int(), 10)

case reflect.Uint, reflect.Uint8, reflect.Uint16,

reflect.Uint32, reflect.Uint64, reflect.Uintptr:

return strconv.FormatUint(v.Uint(), 10)

// ...floating-point and complex cases omitted for brevity...

case reflect.Bool:

return strconv.FormatBool(v.Bool())

case reflect.String:

return strconv.Quote(v.String())

case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:

return v.Type().String() + " 0x" +

strconv.FormatUint(uint64(v.Pointer()), 16)

default: // reflect.Array, reflect.Struct, reflect.Interface

return v.Type().String() + " value"

}

}

形状体: NumField措施报告形状体中成员的数量,Field(i)以reflect.Value类型返回第i个成员

的值。成员列表含有了匿名成员在内的完全成员。通过在path加在“.f”来表示成员路径,朋友儿

时需获得形状体对应的reflect.Type类型信息,含有形状体类型和第i个成员的名字。

Maps: MapKeys措施返回一有三个小reflect.Value类型的slice,每一有三个小都对应map的能只有。和往常一

样,遍历map时顺序是随机的。MapIndex(key)返回map中key对应的value。朋友儿向path添

加“[key]”来表示访问路径。

许多Go语言应用程序都含有了许多循环的数据结果。Display支持类事带环的数据形状是比较棘手

的,时需增加一有三个小额外的记录访问的路径;代价是昂贵的。

指针: Elem措施返回指针指向的变量,还是reflect.Value类型。技术指针是nil,你这些操作也

是安全的,在你这些具体情况下指针是Invalid无效类型,有后后朋友儿能只有用IsNil措施来显式地测试一有三个小

空指针,原本朋友儿能只有打印更要花费的信息。朋友儿在path前面加在“*”,并用括弧含有以防止歧

义。

接口: 再一次,朋友儿使用IsNil措施来测试接口算不算nil,将会完全都不 ,朋友儿能只有调用v.Elem()来

获取接口对应的动态值,有后后打印对应的类型和值。

现在朋友儿的Display函数总算完工了,让朋友儿看看它的表现吧。

没本身生活生活措施来检查未知类型的表示措施,朋友儿被卡住了。这就说 我朋友儿为什么会时需反射的原

因。

reflect 包中原本重要的类型是 Value. 一有三个小 reflect.Value 能只有持一有三个小任意类型的值. 函数

reflect.ValueOf 接受任意的 interface{} 类型, 并返回对应动态类型的reflect.Value. 和

reflect.TypeOf 类事, reflect.ValueOf 返回的结果也是对于具体的类型, 有后后 reflect.Value 也可

以持一有三个小接口值.

v := reflect.ValueOf(3) // a reflect.Value

fmt.Println(v) // "3"

fmt.Printf("%vn", v) // "3"

fmt.Println(v.String()) // NOTE: ""

reflect.ValueOf老要返回一有三个小值的具体类型,将会它是从一有三个小接口值提取的内容。

Display函数的输出如下:

Display e (eval.call):

e.fn = "sqrt"

e.args[0].type = eval.binary

e.args[0].value.op = 47

e.args[0].value.x.type = eval.Var

e.args[0].value.x.value = "A"

e.args[0].value.y.type = eval.Var

e.args[0].value.y.value = "pi"

有前一天朋友儿时需编写一有三个小函数也能防止一类不让说满足普通公共接口的类型的值,也将会是因

为它们并没人取舍的表示措施,将会是在朋友儿设计该函数的前一天还有有哪些类型将会还不位于,

各种具体情况完全都不 将会。

Go语言提供了本身生活机制在运行时更新变量和检查它们的值、调用它们的措施和它们支持的内

在操作,有后后在编译时并我就说 我知道有有哪些变量的具体类型。你这些机制被称为反射。反射也能只有让

朋友儿将类型本身生活作为第一类的值类型防止。

反射是由 reflect 包提供支持. 它定义了一有三个小重要的类型, Type 和 Value. 一有三个小 Type 表示一有三个小

Go类型. 它是一有三个小接口, 有许多措施来区分类型和检查它们的组件, 类事一有三个小形状体的成员或

一有三个小函数的参数等. 唯一能反映 reflect.Type 实现的是接口的类型描述信息(§7.5), 同样的实体

标识了动态类型的接口值.

函数 reflect.TypeOf 接受任意的 interface{} 类型, 并返回对应动态类型的reflect.Type:

t := reflect.TypeOf(3) // a reflect.Type

fmt.Println(t.String()) // "int"

fmt.Println(t) // "int"

// a struct that points to itself

type Cycle struct{ Value int; Tail *Cycle }

var c Cycle

c = Cycle{42, &c}

Display("c", c)

和 reflect.Type 类事, reflect.Value 也满足 fmt.Stringer 接口, 有后后除非 Value 持完全都不 字符串,

有后后 String 就说 我返回具体的类型. 相同, 使用 fmt 包的 %v 标志参数, 将使用 reflect.Values 的

结果格式化.

调用 Value 的 Type 措施将返回具体类型所对应的 reflect.Type:

func Sprint(x interface{}) string {

type stringer interface {

String() string

}

switch x := x.(type) {

case stringer:

return x.String()

case string:

return x

case int:

return strconv.Itoa(x)

// ...similar cases for int16, uint32, and so on...

case bool:

if x {

return "true"

}

return "false"

default:

// array, chan, func, map, pointer, slice, struct

return "???"

}

}

v := reflect.ValueOf(3) // a reflect.Value

x := v.Interface() // an interface{}

i := x.(int) // an int

fmt.Printf("%dn", i) // "3"

Display会永远不停地进行深层递归打印:

Display c (display.Cycle):

c.Value = 42

(*c.Tail).Value = 42

((c.Tail).Tail).Value = 42

(((*c.Tail).Tail).Tail).Value = 42

...ad infinitum...

朋友儿也能只有使用Display函数来显示标准库中类型的内部内部结构形状,类事 *os.File 类型:

Display("os.Stderr", os.Stderr)

// Output:

// Display os.Stderr (*os.File):

// ((os.Stderr).file).fd = 2

// ((os.Stderr).file).name = "/dev/stderr"

// ((os.Stderr).file).nepipe = 0

Go语言的反射形状,看看它能只有给语言增加有哪些表达力,以及在一有三个小至关重要的API是怎么用

反射机制的:一有三个小是fmt包提供的字符串格式功能,原本是类事encoding/json和encoding/xml

提供的针对特定协议的编解码功能。

Display("strangelove", strangelove)调用将显示(strangelove电影对应的中文名是《奇爱博

士》):

Display strangelove (display.Movie):

strangelove.Title = "Dr. Strangelove"

strangelove.Subtitle = "How I Learned to Stop Worrying and Love the Bomb"

strangelove.Year = 1964

strangelove.Color = false

strangelove.Actor["Gen. Buck Turgidson"] = "George C. Scott"

strangelove.Actor["Brig. Gen. Jack D. Ripper"] = "Sterling Hayden"

strangelove.Actor["Maj. T.J. "King" Kong"] = "Slim Pickens"

strangelove.Actor["Dr. Strangelove"] = "Peter Sellers"

strangelove.Actor["Grp. Capt. Lionel Mandrake"] = "Peter Sellers"

strangelove.Actor["Pres. Merkin Muffley"] = "Peter Sellers"

strangelove.Oscars[0] = "Best Actor (Nomin.)"

strangelove.Oscars[1] = "Best Adapted Screenplay (Nomin.)"

strangelove.Oscars[2] = "Best Director (Nomin.)"

strangelove.Oscars[3] = "Best Picture (Nomin.)"

strangelove.Sequel = nil