wns9778.com_威尼斯wns.9778官网

热门关键词: wns9778.com,威尼斯wns.9778官网
wns9778.com > 计算机教程 > 浅谈Go语言中的结构体struct & 接口Interface & 反射

原标题:浅谈Go语言中的结构体struct & 接口Interface & 反射

浏览次数:156 时间:2019-06-08

func (animal Animal) Quack() {
  fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

reflect.Value.Method(n).Call(nil)来调用结构体中的方法

此时v是一个interface,它的值是nil,也就是说其data域为空,但它自身不为nil。

栗子一

在深入浅出 Cocoa 之消息一文中我曾分析过 Objective C 的消息调用过程:

栗子一:

Type的UncommonType中有一个方法表,某个具体类型实现的所有方法都会被收集到这张表中。reflect包中的Method和MethodByName方法都是通过查询这张表实现的。表中的每一项是一个Method,其数据结构如下:

获取变量的值:

与通常以类型层次与继承为根基的面向对象设计(OOP)语言(如C 、Java)不同,Go 的核心思想就是组合(composition)。Go 进一步解耦了对象与操作,实现了真正的鸭子类型(Duck typing):一个对象如果能嘎嘎叫那就能当做鸭子,而不是像 C 或 Java 那样需要类型系统去保证:一个对象先得是只鸭子,然后才能嘎嘎叫。

type Car interface {
 NameGet() string
 Run(n int)
 Stop()
}

type BMW struct {
 Name string
}
func (this *BMW) NameGet() string {
 return this.Name
}
func (this *BMW) Run(n int) {
 fmt.Printf("BMW is running of num is %d n", n)
}
func (this *BMW) Stop() {
 fmt.Printf("BMW is stop n")
}

type Benz struct {
 Name string
}
func (this *Benz) NameGet() string {
 return this.Name
}
func (this *Benz) Run(n int) {
 fmt.Printf("Benz is running of num is %d n", n)
}
func (this *Benz) Stop() {
 fmt.Printf("Benz is stop n")
}
func (this *Benz) ChatUp() {
 fmt.Printf("ChatUp n")
}

func main() {
 var car Car
 fmt.Println(car) // <nil>

 var bmw BMW = BMW{Name: "宝马"}
 car = &bmw
 fmt.Println(car.NameGet()) //宝马
 car.Run(1)     //BMW is running of num is 1
 car.Stop()     //BMW is stop

 benz := &Benz{Name: "大奔"}
 car = benz
 fmt.Println(car.NameGet()) //大奔
 car.Run(2)     //Benz is running of num is 2
 car.Stop()     //Benz is stop
 //car.ChatUp() //ERROR: car.ChatUp undefined (type Car has no field or method ChatUp)
}

Unknown : Quack! Quack! Like a duck!

内存布局

type Error struct {
  errCode uint8
}

func (e *Error) Error() string {
  switch e.errCode {
  default:
    return "unknown error"
  }
}

func test_checkError() {
  var e *Error
  if e == nil {
    fmt.Println("e is nil")
  } else {
    fmt.Println("e is not nil")
  }

  var err error
  err = e

  if err == nil {
    fmt.Println("err is nil")
  } else {
    fmt.Println("err is not nil")
  }
}

reflect.Value.NumField()获取结构体中字段的个数

struct IMethod
{
  String *name;
  String *pkgPath;
  Type *type;
};

栗子三(struct tag 内部实现)

此时v就是一个nil。在底层存储上,它是一个空指针。

下面示例中user1和user2为指针类型,访问的时候编译器会自动把 user1.Name 转为 (*user1).Name

在reflect包中有个KindOf函数,返回一个interface{}的Type,其实该函数就是简单的取Eface中的Type域。

通过反射的来改变变量的值

Bird * aBird = [[Bird alloc] init];
[aBird fly];

转换成interface{}类型

类型转换时的检测就是看Type中的方法表是否包含了InterfaceType的方法表中的所有方法,并把Type方法表中的实现部分拷到Itab的func那张表中。

在go中,首字母大小写有特殊的语法含义,小写包外无法引用。由于需要和其它的系统进行数据交互,例如转成json格式。这个时候如果用属性名来作为键值可能不一定会符合项目要求。tag在转换成其它数据格式的时候,会使用其中特定的字段作为键值。

运行test_checkError()输出:

  通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。

注意事项

Interface实现

func Quack(animal Animal) {
  fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

TypeOf返回接口中保存的值的类型,TypeOf(nil)会返回nil。

Iface的Itab的InterfaceType中也有一张方法表,这张方法表中是接口所声明的方法。其中每一项是一个IMethod,数据结构如下:

type user1 struct {
 name string
 Age int
}

type user2 struct {
 name string
 age int
 sex time.Time
}

type User struct {
 u1 user1 //别名
 user2
 Name string
 Age int
}

func main() {
 var user User
 user.Name = "nick"
 user.u1.Age = 18
 fmt.Println(user) //{{ 18} { 0 {0 0 <nil>}} nick 0}
}
type Duck interface {
  Quack()
}

type Animal struct {
  name string
}

func (animal Animal) Quack() {
  fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

func main() {
  unknownAnimal := Animal{name: "Unknown"}

  var equivalent Duck
  equivalent = unknownAnimal
  equivalent.Quack()
}

一个接口可以嵌套在另外的接口。

下面就具体的语法特性说说我自己的体会。

反射 reflect

Iface中的Itab的func域也是一张方法表,这张表中的每一项就是一个函数指针,也就是只有实现没有声明。

reflect包实现了运行时反射,允许程序操作任意类型的对象。

中对 fly 的调用,编译器通过插入一些代码,将之转换为对方法具体实现 IMP 的调用,这个 IMP 是通过在 Bird 的类结构中的方法链表中查找名称为 fly 的选择子 SEL 对应的具体方法实现找到的,编译器会将消息调用转换为对消息函数 objc_msgSend的调用:

type Car interface {
 NameGet() string
 Run(n int)
 Stop()
}

type Used interface {
 Car
 Cheap()
}

与之不同的情况

reflect.Value.SetXX相关方法,比如:
reflect.Value.SetInt(),设置整数
reflect.Value.SetFloat(),设置浮点数
reflect.Value.SetString(),设置字符串
objc_msgSend(aBird, @selector(fly));

reflect.Value.Interface()

interface Duck {
  void Quack();
}

class SomeAnimal implements Duck {
  String name;

  public SomeAnimal(String name) {
    this.name = name;
  }

  public void Quack() {
    System.out.println(name   ": Quack! Quack! I am a duck!");
  }
}

public class Test {
  public static void main(String []args){
    SomeAnimal unknownAnimal = new SomeAnimal("Unknown");
    Duck equivalent = unknownAnimal;
    equivalent.Quack();
  }
}

多态

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

import "reflect"

type T struct {
 A int
 B string
}

func main() {
 t := T{18, "nick"}
 s := reflect.ValueOf(&t).Elem()
 typeOfT := s.Type()

 for i := 0; i < s.NumField(); i   {
  f := s.Field(i)
  fmt.Printf("%d: %s %s = %vn", i,
   typeOfT.Field(i).Name, f.Type(), f.Interface())
 }

 s.Field(0).SetInt(25)
 s.Field(1).SetString("nicky")
 fmt.Println(t)
}

/*
输出:
0: A int = 18
1: B string = nick
{25 nicky}
*/


import "reflect"

type test struct {
 S1 string
 s2 string
 s3 string
}

var s interface{} = &test{
 S1: "s1",
 s2: "s2",
 s3: "s3",
}

func main() {
 val := reflect.ValueOf(s)
 fmt.Println(val)      //&{s1 s2 s3}
 fmt.Println(val.Elem())    //{s1 s2 s3}
 fmt.Println(val.Elem().Field(0))  //s1
 val.Elem().Field(0).SetString("hehe") //S1大写
}

一个interface在没有进行初始化时,对应的值是nil。也就是说:

type User struct {
 Name stirng
 Age int  
}

type Lover struct {
  User
  sex time.Time
  int
  Age int
}

interface 实际上就是一个结构体,包含两个成员。其中一个成员是指向具体数据的指针,另一个成员中包含了类型信息。空接口和带方法的接口略有不同,下面分别是空接口和带方法的接口是使用的数据结构:

var t int
var x interface{}
x = t

y = x.(int)  //转成int
y, ok = x.(int) //转成int,不报错

运行上面的代码输出:

struct类型是值类型。

下面来看个例子就明白了:
Go语言中的error类型实际上是抽象了Error()方法的error接口:

匿名字段

无论是 Objective C 的消息机制还是 Qt 中的 Signal/Slot 机制,可以说都是在尝试将对象本身(数据)与对对象的操作(消息)解耦,但 Go 将这个工作在语言层面做得更加彻底,这样不仅避免多重继承问题,还体现出面向对象设计中最要紧的事情:对象间的消息传递。

  调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。

先看Eface,它是interface{}底层使用的数据结构。数据域中包含了一个void*指针,和一个类型结构体的指针。interface{}扮演的角色跟C语言中的void*是差不多的,Go中的任何对象都可以表示为interface{}。不同之处在于,interface{}中有类型信息,于是可以实现反射。

如果一个变量含有了多个interface类型的方法,那么这个变量就实现了多个接口;如果一个变量只含有了1个interface的方部分方法,那么这个变量没有实现这个接口。

struct Method
{
  String *name;
  String *pkgPath;
  Type  *mtyp;
  Type *typ;
  void (*ifn)(void);
  void (*tfn)(void);
};
type Car interface {
 NameGet() string
 Run(n int)
 Stop()
}

实现

struct中的所有字段在内存是连续的

概览

struct定义

为:

interface类型默认是一个指针。

浅显地了解了一下 Go,发现 Go 语法的设计非常简洁,易于理解。正应了 Go 语言之父 Rob Pike 说的那句“Less is more”—— 大道至简。

栗子二(通过反射修改结构体)

是不是就和普通方法并无二致了?

var user User
 user.Name = "nick"
 user.Age = 18
 user.mess = "lover"

 fmt.Println(user)     //{nick 18 lover}
 fmt.Printf("Name:%pn", &user.Name) //Name:0xc420016180
 fmt.Printf("Age: %pn", &user.Age) //Age: 0xc420016190
 fmt.Printf("mess:%pn", &user.mess) //mess:0xc420016198 8字节为内存对齐

Iface和Eface略有不同,它是带方法的interface底层使用的数据结构。data域同样是指向原始数据的,Itab中不仅存储了Type信息,而且还多了一个方法表fun[]。一个Iface中的具体类型中实现的方法会被拷贝到Itab的fun数组中。

ValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。

type error interface {
  Error() string
}
reflect.ValueOf(x).Int()
reflect.ValueOf(x).Float() 
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()

不同类型数据的类型信息结构体并不完全一致,Type是类型信息结构体中公共的部分,其中size描述类型的大小,UncommonType是指向一个函数指针的数组,收集了这个类型的具体实现的所有方法。

type Stringer interface {
 String() string
}

type Mystruct interface {

}
type Mystruct2 struct {

}
func (this *Mystruct2) String() string {
 return ""
}

func main() {
 var v Mystruct
 var v2 Mystruct2
 v = &v2

 if sv, ok := v.(Stringer); ok {
  fmt.Printf("%v implements String(): %sn", sv.String());
 }
}

下面用 Java 语言来实现:

结构体struct

struct Eface
{
  Type*  type;
  void*  data;
};
struct Iface
{
  Itab*  tab;
  void*  data;
};

struct Itab
{
  InterfaceType*  inter;
  Type*  type;
  Itab*  link;
  int32  bad;
  int32  unused;
  void  (*fun[])(void);
};

struct Type
{
  uintptr size;
  uint32 hash;
  uint8 _unused;
  uint8 align;
  uint8 fieldAlign;
  uint8 kind;
  Alg *alg;
  void *gc;
  String *string;
  UncommonType *x;
  Type *ptrto;
};

Interface定义

您可能感兴趣的文章:

构造函数

interface

Interface嵌套

e is nil
err is not nil

type name1 struct {
 int
 string
}

func (this *name1) String() string {
 return fmt.Sprintf("This is String(%s).", this.string)
}

func main() {
 n := new(name1)
 fmt.Println(n) //This is String().
 n.string = "suoning"
 d := fmt.Sprintf("%s", n) //This is String(suoning).
 fmt.Println(d)
}

 跟上面的Method结构体对比可以发现,这里是只有声明没有实现的。

import "reflect"

type NotknownType struct {
 S1 string
 S2 string
 S3 string
}

func (n NotknownType) String() string {
 return n.S1   " & "   n.S2   " & "   n.S3
}

var secret interface{} = NotknownType{"Go", "C", "Python"}

func main() {
 value := reflect.ValueOf(secret)
 fmt.Println(value) //Go & C & Python
 typ := reflect.TypeOf(secret)
 fmt.Println(typ) //main.NotknownType

 knd := value.Kind()
 fmt.Println(knd) // struct

 for i := 0; i < value.NumField(); i   {
  fmt.Printf("Field %d: %vn", i, value.Field(i))
 }

 results := value.Method(0).Call(nil)
 fmt.Println(results) // [Go & C & Python]
}
var v interface{}

方法的访问控制也是通过大小写控制。

两相比较就能看出:Go 将对象与对其的操作(方法或函数)解耦得更彻底。Go 并不需要一个对象通过类型系统来保证实现了某个接口(is a),而只需要这个对象实现了某个接口的方法即可(like a),而且类型声明与方法声明或实现也是松耦合的形式。如果稍微转换一下方法的实现方式:

栗子二:

var obj *T
var v interface{}
v = obj

func TypeOf(i interface{}) Type

有如下代码:

方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct。

本文由wns9778.com发布于计算机教程,转载请注明出处:浅谈Go语言中的结构体struct & 接口Interface & 反射

关键词: wns9778.com

上一篇:Django-admin管理工具

下一篇:没有了