ホームページ  >  記事  >  バックエンド開発  >  GolangのStruct(構造体)について詳しく解説

GolangのStruct(構造体)について詳しく解説

青灯夜游
青灯夜游転載
2022-11-17 20:49:123093ブラウズ

GolangのStruct(構造体)について詳しく解説

Go 言語は struct のサポートを提供します。struct、中国語訳は structural と呼ばれ、配列と同様に複合型です。 、参照型ではありません。 [関連する推奨事項: Go ビデオ チュートリアル ]

Go 言語の構造体は、C 言語の構造体や他のオブジェクト指向プログラミング言語のクラス (クラス) に似ています。 ) とメソッドを定義できますが、それらは非常に異なるものでもあり、それらの違いを区別するには詳細な研究が必要です。

複合型と参照型の違いに注意してください。これは、値の受け渡しと参照の受け渡しの違いでもあります。

定義

構造体を定義するには、struct キーワードを使用します。構造体のメンバーは、構造体のフィールドまたは属性と呼ばれます。

type Member struct {
    id          int
    name, email string
    gender, age int
}

上記のコードでは、5 つのフィールドを含む構造体を定義しています。同じ型 nameemailgender が存在することがわかります。と age は同じ行で定義されていますが、プログラミングのより良い習慣は、次のように各行にフィールドを 1 つだけ定義することです。

type Member struct {
    id     int
    name   string
    email  string
    gender int
    age    int
}

もちろん、構造体にフィールドを含めることもできません。フィールド。空の構造体と呼ばれ、struct{} は空の構造体を表します。空の構造体を直接定義することは意味がありませんが、同時プログラミングではチャネル間の通信を使用できることに注意してください。A struct{}セマフォとして機能します。

ch := make(chan struct{})
ch <- struct{}{}

使用法

上記の例では、Member 構造タイプを定義し、このカスタム タイプの変数を作成できます。

変数を直接定義します。この使用方法ではフィールドに初期値が割り当てられないため、すべてのフィールドには独自の型のゼロ値が自動的に割り当てられます。たとえば、# の値##name は null 文字です。文字列 ""、年齢の値は 0 です。

var m1 Member//所有字段均为空值
リテラルを使用して変数を作成します。このようにして、中括弧内の構造体のメンバーに初期値を割り当てることができます。初期値を割り当てる方法は 2 つあります。1 つは構造体内のフィールドの順序で値を割り当てます。以下のコードの

m2 はこのメソッドを使用します。このメソッドでは、すべてのフィールドに値を割り当てる必要があるため、フィールドが多すぎる場合は、各フィールドに値を割り当てる必要があります。これは非常に面倒です。もう 1 つの方法は、次のコードで変数 m3 を作成するなど、フィールド名を使用してフィールドの割り当てを指定することです。このメソッドでは、その他の未指定のフィールドについては、初期化値としてフィールド タイプのゼロ値を使用します。

var m2 = Member{1,"小明","xiaoming@163.com",1,18} // 简短变量声明方式:m2 := Member{1,"小明","xiaoming@163.com",1,18}
var m3 = Member{id:2,"name":"小红"}// 简短变量声明方式:m3 := Member{id:2,"name":"小红"}
フィールドへのアクセス

カンマ

(.) を使用して変数名を通じて構造体型のフィールドにアクセスしたり、フィールドに値を割り当てたりできます。または、フィールドに対してアドレス (&) 操作を実行することもできます。

fmt.Println(m2.name)//输出:小明
m3.name = "小花"
fmt.Println(m3.name)//输出:小花

age := &m3.age
*age = 20
fmt.Println(m3.age)//20

ポインタ構造体

構造体は、配列と同様、値によって渡されます。たとえば、配列または構造体が実パラメータとして関数の仮パラメータに渡されると、コピーはしたがって、パフォーマンスを向上させるために、通常、配列は関数に直接渡されず、代わりにスライス (参照型) が使用されます。関数に構造体を渡す場合、

ポインタ構造体を使用できます。

ポインタ構造体、つまり構造体へのポインタ。構造体変数を宣言するときは、構造体型の前に * 記号を追加して、次のように構造体へのポインタを宣言します。 ##ポインタ型は参照型であることに注意してください。構造体ポインタを宣言する場合、初期化されていない場合、初期値は nil になります。初期化後にのみ、フィールドにアクセスしたり、値を割り当てたりすることができます。

var m1 *Member
m1.name = "小明"//错误用法,未初始化,m1为nil

m1 = &Member{}
m1.name = "小明"//初始化后,结构体指针指向某个结构体地址,才能访问字段,为字段赋值。
さらに、Go の組み込み new() 関数を使用すると、メモリを割り当てて構造体を初期化し、割り当てられたメモリ ポインタを返すことができます。初期化されているため、フィールドに直接アクセスできます。 。

var m2 = new(Member)
m2.name = "小红"

構造体を関数に転送すると、構造体のコピーが作成されるだけであることがわかっています。関数内で構造体のフィールド値を変更しても、外部構造体には影響しません。構造体を転送する場合 ポインタが関数に渡された場合、関数内でポインタを使用して構造体に加えられた変更は、ポインタが指す構造体に影響を与えます。
func main() {
    m1 := Member{}
    m2 := new(Member)
    Change(m1,m2)
    fmt.Println(m1,m2)
}

func Change(m1 Member,m2 *Member){
    m1.Name = "小明"
    m2.Name = "小红"
}

Visibility

上記の例では、構造体フィールド名の最初の文字を小文字に定義しました。これは、これらのフィールドが

パッケージの外では表示されないことを意味します

したがって、他のパッケージからはアクセスできず、パッケージ内でのみアクセスできます。

次の例では、メンバー パッケージで Member を宣言し、メイン パッケージで変数を作成していますが、構造体のフィールドはパッケージの外からは見えないため、初期値を割り当てることができません。フィールドに追加しても押せない フィールドがインデックスによって割り当てられている場合でも、割り当てられている場合でも、パニック エラーが発生します。 <pre class="brush:js;toolbar:false;">package member type Member struct { id int name string email string gender int age int } package main fun main(){ var m = member.Member{1,&quot;小明&quot;,&quot;xiaoming@163.com&quot;,1,18}//会引发panic错误 }</pre> したがって、あるパッケージ内の別のパッケージにある構造体のフィールドにアクセスする場合は、次のような大文字で始まる変数、つまりエクスポートされた変数である必要があります。

type Member struct {
    id     int
    name   string
    email  string
    gender int
    age    int
}

タグ

構造体フィールドを定義する場合、フィールド名とデータ型に加えて、バッククォートを使用して構造体フィールドのメタ情報を宣言することもできます。このメタ情報はタグと呼ばれます。は、コンパイル段階でフィールドに関連付けるために使用されます。たとえば、上記の例の構造を次のように変更します。
type Member struct {
    Id     int    `json:"id,-"`
    Name   string `json:"name"`
    Email  string `json:"email"`
    Gender int    `json:"gender,"`
    Age    int    `json:"age"`
}

上面例子演示的是使用encoding/json包编码或解码结构体时使用的Tag信息。

Tag由反引号括起来的一系列用空格分隔的key:"value"键值对组成,如:

Id int `json:"id" gorm:"AUTO_INCREMENT"`

特性

下面总结几点结构体的相关特性:

值传递

结构体与数组一样,是复合类型,无论是作为实参传递给函数时,还是赋值给其他变量,都是值传递,即复一个副本。

没有继承

Go语言是支持面向对象编程的,但却没有继承的概念,在结构体中,可以通过组合其他结构体来构建更复杂的结构体。

结构体不能包含自己

一个结构体,并没有包含自身,比如Member中的字段不能是Member类型,但却可能是*Member。

方法

在Go语言中,将函数绑定到具体的类型中,则称该函数是该类型的方法,其定义的方式是在func与函数名称之间加上具体类型变量,这个类型变量称为方法接收器,如:

注意,并不是只有结构体才能绑定方法,任何类型都可以绑定方法,只是我们这里介绍将方法绑定到结构体中。

func setName(m Member,name string){//普通函数
    m.Name = name
}

func (m Member)setName(name string){//绑定到Member结构体的方法
    m.Name = name
}

从上面的例子中,我们可以看出,通过方法接收器可以访问结构体的字段,这类似其他编程语言中的this关键词,但在Go语言中,只是一个变量名而已,我们可以任意命名方法接收器

调用结构体的方法,与调用字段一样:

m := Member{}
m.setName("小明")
fmt.Println(m.Name)//输出为空

上面的代码中,我们会很奇怪,不是调用setName()方法设置了字段Name的值了吗?为什么还是输出为空呢?

这是因为,结构体是值传递,当我们调用setName时,方法接收器接收到是只是结构体变量的一个副本,通过副本对值进行修复,并不会影响调用者,因此,我们可以将方法接收器定义为指针变量,就可达到修改结构体的目的了。

func (m *Member)setName(name string){/将Member改为*Member
    m.Name = name
}

m := Member{}
m.setName("小明")
fmt.Println(m.Name)//小明

方法和字段一样,如果首字母为小写,则只允许在包内可见,在其他包中是无法访问的,因此,如果要在其他包中访问setName,则应该将方法名改为SetName

组合

我们知道,结构体中并没有继承的概念,其实,在Go语言中也没有继承的概念,Go语言的编程哲学里,推荐使用组合的方式来达到代码复用效果。

什么是组合

组合,可以理解为定义一个结构体中,其字段可以是其他的结构体,这样,不同的结构体就可以共用相同的字段。

注意,在记得我们前面提过的,结构体不能包含自身,但可能包含指向自身的结构体指针。

例如,我们定义了一个名为Animal表示动物,如果我们想定义一个结构体表示猫,如:

type Animal struct {
    Name   string  //名称
    Color  string  //颜色
    Height float32 //身高
    Weight float32 //体重
    Age    int     //年龄
}
//奔跑
func (a Animal)Run() {
    fmt.Println(a.Name + "is running")
}
//吃东西
func (a Animal)Eat() {
    fmt.Println(a.Name + "is eating")
}

type Cat struct {
    a Animal
}

func main() {
    var c = Cat{
	    a: Animal{
            Name:   "猫猫",
            Color:  "橙色",
            Weight: 10,
            Height: 30,
            Age:    5,
        },
    }
    fmt.Println(c.a.Name)
    c.a.Run()
}

可以看到,我们定义Cat结构体时,可以把Animal结构体作为Cat的字段。

匿名字段

上面的例子,我们看到,把Animal结构体作为Cat的字段时,其变量名为a,所以我们访问Animal的方法时,语法为c.a.Run(),这种通过叶子属性访问某个字段类型所带的方法和字段用法非常繁琐。

Go语言支持直接将类型作为结构体的字段,而不需要取变量名,这种字段叫匿名字段,如:

type Lion struct {
	Animal //匿名字段
}

func main(){
    var lion = Lion{
        Animal{
            Name:  "小狮子",
            Color: "灰色",
        },
    }
    lion.Run()
    fmt.Println(lion.Name)
}

通过上面例子,可以看到,通过匿名字段组合其他类型,而后访问匿名字段类型所带的方法和字段时,不需要使用叶子属性,非常方便。

小结

在Go语言编程中,结构体大概算是使用得最多的数据类型了,通过定义不同字段和方法的结构体,抽象组合不同的结构体,这大概便是Go语言中对面向对象编程了。

原文地址:https://juejin.cn/post/6844903814168838151

更多编程相关知识,请访问:编程视频!!

以上がGolangのStruct(構造体)について詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。