获取和设置结构体的属性值

packagemainimport("fmt""reflect")typeUserstruct{Emailstring`mcl:"email"`Namestring`mcl:"name"`Ageint`mcl:"age"`Githubstring`mcl:"github" default:"a8m"`}funcmain(){u:=&User{Name:"Ariel Mashraki"}// Elem returns the value that the pointer u points to.//得到u的指针v:=reflect.ValueOf(u).Elem()f:=v.FieldByName("Github")// make sure that this field is defined, and can be changed.//确保field 是有效的 也是可以写的if!f.IsValid()||!f.CanSet(){return}iff.Kind()!=reflect.String||f.String()!=""{return}f.SetString("a8m")fmt.Printf("Github username was changed to: %q\n",u.Github)}

向不知都类型的slice中添加字符 使用场景decoder

packagemainimport("fmt""io""reflect")funcmain(){var(a[]stringb[]interface{}c[]io.Writer)fmt.Println(fill(&a),a)// passfmt.Println(fill(&b),b)// passfmt.Println(fill(&c),c)// fail}funcfill(iinterface{})error{v:=reflect.ValueOf(i)ifv.Kind()!=reflect.Ptr{returnfmt.Errorf("non-pointer %v",v.Type())}// get the value that the pointer v points to.v=v.Elem()ifv.Kind()!=reflect.Slice{returnfmt.Errorf("can't fill non-slice value")}v.Set(reflect.MakeSlice(v.Type(),3,3))// validate the type of the slice. see below.if!canAssign(v.Index(0)){returnfmt.Errorf("can't assign string to slice elements")}fori,w:=range[]string{"foo","bar","baz"}{v.Index(i).Set(reflect.ValueOf(w))}returnnil}// we accept strings, or empty interfaces.funccanAssign(vreflect.Value)bool{returnv.Kind()==reflect.String||(v.Kind()==reflect.Interface&&v.NumMethod()==0)}

给数字类型变量设置值,场景decoder

packagemainimport("fmt""reflect")constn=255funcmain(){var(aint8bint16cuintdfloat32estring)fmt.Println(fill(&a),a)fmt.Println(fill(&b),b)fmt.Println(fill(&c),c)fmt.Println(fill(&d),c)fmt.Println(fill(&e),e)}funcfill(iinterface{})error{v:=reflect.ValueOf(i)ifv.Kind()!=reflect.Ptr{returnfmt.Errorf("non-pointer %v",v.Type())}// get the value that the pointer v points to.v=v.Elem()switchv.Kind(){casereflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:ifv.OverflowInt(n){returnfmt.Errorf("can't assign value due to %s-overflow",v.Kind())}v.SetInt(n)casereflect.Uint,reflect.Uint8,reflect.Uint16,reflect.Uint32,reflect.Uint64,reflect.Uintptr:ifv.OverflowUint(n){returnfmt.Errorf("can't assign value due to %s-overflow",v.Kind())}v.SetUint(n)casereflect.Float32,reflect.Float64:ifv.OverflowFloat(n){returnfmt.Errorf("can't assign value due to %s-overflow",v.Kind())}v.SetFloat(n)default:returnfmt.Errorf("can't assign value to a non-number type")}returnnil}

Decode key-value 到 map

packagemainimport("fmt""reflect""strconv""strings")funcmain(){var(m0=make(map[int]string)m1=make(map[int64]string)m2map[interface{}]stringm3map[interface{}]interface{}m4map[bool]string)s:="1=foo,2=bar,3=baz"fmt.Println(decodeMap(s,&m0),m0)// passfmt.Println(decodeMap(s,&m1),m1)// passfmt.Println(decodeMap(s,&m2),m2)// passfmt.Println(decodeMap(s,&m3),m3)// passfmt.Println(decodeMap(s,&m4),m4)// fail}funcdecodeMap(sstring,iinterface{})error{v:=reflect.ValueOf(i)ifv.Kind()!=reflect.Ptr{returnfmt.Errorf("non-pointer %v",v.Type())}// get the value that the pointer v points to.v=v.Elem()t:=v.Type()// allocate a new map, if v is nil. see: m2, m3, m4.ifv.IsNil(){v.Set(reflect.MakeMap(t))}// assume that the input is valid.for_,kv:=rangestrings.Split(s,","){s:=strings.Split(kv,"=")n,err:=strconv.Atoi(s[0])iferr!=nil{returnfmt.Errorf("failed to parse number: %v",err)}k,e:=reflect.ValueOf(n),reflect.ValueOf(s[1])// get the type of the key.kt:=t.Key()if!k.Type().ConvertibleTo(kt){returnfmt.Errorf("can't convert key to type %v",kt.Kind())}k=k.Convert(kt)// get the element type.et:=t.Elem()ifet.Kind()!=v.Kind()&&!e.Type().ConvertibleTo(et){returnfmt.Errorf("can't assign value to type %v",kt.Kind())}v.SetMapIndex(k,e.Convert(et))}returnnil}

Decode key-value 到结构体

packagemainimport("fmt""reflect""strings")typeUserstruct{NamestringGithubstringprivatestring}funcmain(){var(v0Userv1*Userv2=new(User)v3struct{Namestring}s="Name=Ariel,Github=a8m")fmt.Println(decode(s,&v0),v0)// passfmt.Println(decode(s,v1),v1)// failfmt.Println(decode(s,v2),v2)// passfmt.Println(decode(s,v3),v3)// failfmt.Println(decode(s,&v3),v3)// pass}funcdecode(sstring,iinterface{})error{v:=reflect.ValueOf(i)ifv.Kind()!=reflect.Ptr||v.IsNil(){returnfmt.Errorf("decode requires non-nil pointer")}// get the value that the pointer v points to.v=v.Elem()// assume that the input is valid.for_,kv:=rangestrings.Split(s,","){s:=strings.Split(kv,"=")f:=v.FieldByName(s[0])// make sure that this field is defined, and can be changed.if!f.IsValid()||!f.CanSet(){continue}// assume all the fields are type string.f.SetString(s[1])}returnnil}

Encode 结构体到 key-value 对

packagemainimport("fmt""reflect""strings")typeUserstruct{Emailstring`kv:"email,omitempty"`Namestring`kv:"name,omitempty"`Githubstring`kv:"github,omitempty"`privatestring}funcmain(){var(u=User{Name:"Ariel",Github:"a8m"}v=struct{A,B,Cstring}{"foo","bar","baz",}w=&User{})fmt.Println(encode(u))fmt.Println(encode(v))fmt.Println(encode(w))}// this example supports only structs, and assume their// fields are type string.funcencode(iinterface{})(string,error){v:=reflect.ValueOf(i)t:=v.Type()ift.Kind()!=reflect.Struct{return"",fmt.Errorf("type %s is not supported",t.Kind())}vars[]stringfori:=0;i<t.NumField();i++{f:=t.Field(i)// skip unexported fields. from godoc:// PkgPath is the package path that qualifies a lower case (unexported)// field name. It is empty for upper case (exported) field names.iff.PkgPath!=""{continue}fv:=v.Field(i)key,omit:=readTag(f)// skip empty values when "omitempty" set.ifomit&&fv.String()==""{continue}s=append(s,fmt.Sprintf("%s=%s",key,fv.String()))}returnstrings.Join(s,","),nil}funcreadTag(freflect.StructField)(string,bool){val,ok:=f.Tag.Lookup("kv")if!ok{returnf.Name,false}opts:=strings.Split(val,",")omit:=falseiflen(opts)==1{omit=opts[1]=="omitempty"}returnopts[0],omit}

检查某个对象是否实现 interface 方法

packagemainimport("fmt""reflect")typeMarshalerinterface{MarshalKV()(string,error)}typeUserstruct{Emailstring`kv:"email,omitempty"`Namestring`kv:"name,omitempty"`Githubstring`kv:"github,omitempty"`privatestring}func(uUser)MarshalKV()(string,error){returnfmt.Sprintf("name=%s,email=%s,github=%s",u.Name,u.Email,u.Github),nil}funcmain(){fmt.Println(encode(User{"boring","Ariel","a8m",""}))fmt.Println(encode(&User{Github:"posener",Name:"Eyal",Email:"boring"}))}varmarshalerType=reflect.TypeOf(new(Marshaler)).Elem()funcencode(iinterface{})(string,error){t:=reflect.TypeOf(i)if!t.Implements(marshalerType){return"",fmt.Errorf("encode only supports structs that implement the Marshaler interface")}m,_:=reflect.ValueOf(i).Interface().(Marshaler)returnm.MarshalKV()}

调用函数

不带参数没有返回值调用函数

packagemainimport("fmt""reflect")typeAstruct{}func(A)Hello(){fmt.Println("World")}funcmain(){// ValueOf returns a new Value, which is the reflection interface to a Go value.v:=reflect.ValueOf(A{})m:=v.MethodByName("Hello")ifm.Kind()!=reflect.Func{return}m.Call(nil)}

动态调用函数,类似于 template/text 包

可以用在写自己的动态路由

packagemainimport("fmt""html/template""reflect""strconv""strings")funcmain(){funcs:=template.FuncMap{"trim":strings.Trim,"lower":strings.ToLower,"repeat":strings.Repeat,"replace":strings.Replace,}fmt.Println(eval(`{{ "hello" 4 | repeat }}`,funcs))fmt.Println(eval(`{{ "Hello-World" | lower }}`,funcs))fmt.Println(eval(`{{ "foobarfoo" "foo" "bar" -1 | replace }}`,funcs))fmt.Println(eval(`{{ "-^-Hello-^-" "-^" | trim }}`,funcs))}// evaluate an expression. note that this implemetation is assuming that the// input is valid, and also very limited. for example, whitespaces are not allowed// inside a quoted string.funceval(sstring,funcstemplate.FuncMap)(string,error){args,name:=parseArgs(s)fn,ok:=funcs[name]if!ok{return"",fmt.Errorf("function %s is not defined",name)}v:=reflect.ValueOf(fn)t:=v.Type()iflen(args)!=t.NumIn(){return"",fmt.Errorf("invalid number of arguments. got: %v, want: %v",len(args),t.NumIn())}argv:=make([]reflect.Value,len(args))// go over the arguments, validate and build them.// note that we support only int, and string in this simple example.fori:=rangeargv{varargTypereflect.Kind// if the argument "i" is string.ifstrings.HasPrefix(args[i],"\""){argType=reflect.Stringargv[i]=reflect.ValueOf(strings.Trim(args[i],"\""))}else{argType=reflect.Int// assume that the input is valid.num,_:=strconv.Atoi(args[i])argv[i]=reflect.ValueOf(num)}ift.In(i).Kind()!=argType{return"",fmt.Errorf("Invalid argument. got: %v, want: %v",argType,t.In(i).Kind())}}result:=v.Call(argv)// in real-world code, we validate it before executing the function,// using the v.NumOut() method. similiar to the text/template package.iflen(result)!=1||result[0].Kind()!=reflect.String{return"",fmt.Errorf("function %s must return a one string value",name)}returnresult[0].String(),nil}// parseArgs is an auxiliary function, that extract the function and its// parameter from the given expression.funcparseArgs(sstring)([]string,string){args:=strings.Split(strings.Trim(s,"{ }"),"|")returnstrings.Split(strings.Trim(args[0]," ")," "),strings.Trim(args[len(args)-1]," ")}