go语言中的反射的使用

今天尝试了一下使用go语言中的反射来将struct类型转换成xml,结果相当纠结。首先去看了一下go的reflect包的实现,根据go的规则,首先应该去看一个NewXXX的方法,结果发现了一个叫NewValue的方法,通过这个方法我们能够得到一个Value接口。另外我们还应该注意到,go的反映实现中将Type和Value分开了,于是还有另外一个接口Type.


  type Value interface {
    // Type returns the value's type.
    Type() Type

    // Interface returns the value as an interface{}.
    Interface() interface{}

    // CanSet returns whether the value can be changed.
    // Values obtained by the use of non-exported struct fields
    // can be used in Get but not Set.
    // If CanSet() returns false, calling the type-specific Set
    // will cause a crash.
    CanSet() bool

    // SetValue assigns v to the value; v must have the same type as the value.
    SetValue(v Value)

    // Addr returns a pointer to the underlying data.
    // It is for advanced clients that also
    // import the "unsafe" package.
    Addr() uintptr

    // Method returns a FuncValue corresponding to the value's i'th method.
    // The arguments to a Call on the returned FuncValue
    // should not include a receiver; the FuncValue will use
    // the value as the receiver.
    Method(i int) *FuncValue
    // contains unexported methods
}

type Type interface {
    // PkgPath returns the type's package path.
    // The package path is a full package import path like "container/vector".
    // PkgPath returns an empty string for unnamed types.
    PkgPath() string

    // Name returns the type's name within its package.
    // Name returns an empty string for unnamed types.
    Name() string

    // String returns a string representation of the type.
    // The string representation may use shortened package names
    // (e.g., vector instead of "container/vector") and is not
    // guaranteed to be unique among types.  To test for equality,
    // compare the Types directly.
    String() string

    // Size returns the number of bytes needed to store
    // a value of the given type; it is analogous to unsafe.Sizeof.
    Size() uintptr

    // Bits returns the size of the type in bits.
    // It is intended for use with numeric types and may overflow
    // when used for composite types.
    Bits() int

    // Align returns the alignment of a value of this type
    // when allocated in memory.
    Align() int

    // FieldAlign returns the alignment of a value of this type
    // when used as a field in a struct.
    FieldAlign() int

    // Kind returns the specific kind of this type.
    Kind() Kind

    // For non-interface types, Method returns the i'th method with receiver T.
    // For interface types, Method returns the i'th method in the interface.
    // NumMethod returns the number of such methods.
    Method(int) Method
    NumMethod() int
    // contains unexported methods
}

然后包里还根据每种数据类型都定义了相应的XXXType和XXXValue结构体,那么我们如何得到这些结构体的实例呢?因为只有得到这些构体的实例才能利用反映来操纵这些数据。于是很丑陋的地方来了,你需要使用.(type)来得到当前这个变量的类型,而这个使用必须在switch中,请看下面的例子


func test(data interface{}) {
    switch rvalue := reflect.NewValue(data).(type) {
    default:
        println(reflect.Typeof(rvalue).String())
    }   
    switch rtype := reflect.Typeof(data).(type) {
    default:
        println(reflect.Typeof(rtype).String())
    }   
    switch rvalue := data.(type) {
    default:
        println(reflect.Typeof(rvalue).String())
    }   
}

NewValue创建了一个data的Value接口,但是这个接口是谁实现的呢?通过.(type)就可以得到。

Typeof创建了一个data的Type接口,同样的这个接口也可以通过.(type)得到它的实现。

而如果不使用reflect里的任何方法直接对变量data使用.(type),那么就会得到这个接口的实现。

因此从这里我们可以看到go语言中使用反映需要两个条件,一个是通过type得到它的实现变量,一个是通过reflect里提供的接口来进行操作。


但是很恶心的是,每次操作都必须通过switch case来执行,比如最后我的代码就变成了如下这个样子:


func asString(val interface{}) string {
	switch value := reflect.NewValue(val).(type) {
	case *reflect.StringValue:
		return value.Get()
	case *reflect.BoolValue:
		return strconv.Btoa(value.Get())
	case *reflect.IntValue:
		return strconv.Itoa64(value.Get())
	default:
		panic("invalid type for xml generator")
	}
	return ""
}

func toXml(val interface{}, writer io.Writer) {
	switch value := reflect.NewValue(val).(type) {
	case *reflect.StructValue:
		writer.Write([]byte("<"))
		writer.Write([]byte(reflect.Typeof(val).Name()))
		switch vtype := reflect.NewValue(val).Type().(type) {
		case *reflect.StructType:
			//compose attribute
			for i := 0; i < value.NumField(); i++ {
				field := vtype.Field(i)
				if field.Tag != "attr" {
					continue
				}
				writer.Write([]byte(" "))
				writer.Write([]byte(field.Name))
				writer.Write([]byte("=/""))
				writer.Write([]byte(asString(value.Field(i).Interface())))
				writer.Write([]byte("/""))
			}
			writer.Write([]byte(">"))
			//compose children
			for i := 0; i < value.NumField(); i++ {
				field := vtype.Field(i)
				if field.Tag == "attr" {
					continue
				}
				switch child := value.Field(i).(type) {
				case *reflect.StructValue:
					toXml(child.Interface(), writer)
				case *reflect.ArrayValue:
				case *reflect.SliceValue:
					writer.Write([]byte("<"))
					writer.Write([]byte(field.Name))
					writer.Write([]byte(">"))
					for i := 0; i < child.Len(); i++ {
						switch elem := child.Elem(i).(type) {
						case *reflect.StructValue:
							toXml(elem.Interface(), writer)
						default:
							panic("invalid type for array, only struct supported")
						}
					}
					writer.Write([]byte(""))
				default:
					writer.Write([]byte(asString(child.Interface())))
				}
			}
			//compose end
			writer.Write([]byte(""))
		default:
			panic("invalid type: only array, slice, struct and primitive type supported")
		}
	default:
		panic("invalid type, only struct supported")
	}
}

本文来自:ITEYE博客

感谢作者:javatgo

查看原文:go语言中的反射的使用

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。