수색…
소개
구조체는 함께 묶인 다양한 변수의 집합입니다. 구조체 자체는 변수가 들어있는 패키지 일 뿐이며 쉽게 액세스 할 수 있습니다.
C와는 달리 Go 구조체는 메소드를 첨부 할 수 있습니다. 또한 인터페이스를 구현할 수 있습니다. Go의 구조체는 객체와 유사하지만 상속과 같은 객체 지향 언어로 알려진 주요 기능이 누락되어 있습니다.
기본 선언
기본 구조체는 다음과 같이 선언됩니다.
type User struct {
FirstName, LastName string
Email string
Age int
}
각 값을 필드라고합니다. 필드는 보통 한 줄에 하나씩 쓰여지며 그 필드의 이름은 그 유형보다 선행합니다. 위의 예제에서 FirstName
과 LastName
과 같은 유형의 연속 필드를 결합 할 수 있습니다.
공개되지 않은 필드와 비공개 필드 (비공개 대 공용)
이름이 대문자로 시작되는 구조 필드가 내보내집니다. 다른 모든 이름은 안보이게됩니다.
type Account struct {
UserID int // exported
accessToken string // unexported
}
비공개 필드는 동일한 패키지 내의 코드에서만 액세스 할 수 있습니다. 따라서 다른 패키지의 필드에 액세스하는 경우 이름은 대문자로 시작해야합니다.
package main
import "bank"
func main() {
var x = &bank.Account{
UserID: 1, // this works fine
accessToken: "one", // this does not work, since accessToken is unexported
}
}
그러나 bank
패키지에서 UserId와 accessToken에 문제없이 액세스 할 수 있습니다.
패키지 bank
는 다음과 같이 구현 될 수 있습니다.
package bank
type Account struct {
UserID int
accessToken string
}
func ProcessUser(u *Account) {
u.accessToken = doSomething(u) // ProcessUser() can access u.accessToken because
// it's defined in the same package
}
합성 및 삽입
컴포지션은 상속에 대한 대안을 제공합니다. 구조체는 선언에 이름으로 다른 유형을 포함 할 수 있습니다.
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
위의 예제에서 AuthenticatedRequest
에는 네 개의 공개 멤버 인 Resource
, Request
, Username
및 Password
됩니다.
복합 구조체는 인스턴스화되어 일반 구조체와 같은 방식으로 사용될 수 있습니다.
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
퍼가기
앞의 예에서 Request
는 포함 된 필드입니다. 컴포지션은 다른 유형을 임베드하여 수행 할 수도 있습니다. 예를 들어, 더 많은 기능을 가진 Struct을 장식하는 데 유용합니다. 예를 들어, Resource 예제를 계속 진행하면서 Resource 필드의 내용을 http://
또는 https://
접두어로 형식화하는 함수가 필요합니다. 우리는 두 가지 옵션이 있습니다 : AuthenticatedRequest에 새로운 메소드를 생성하거나 다른 구조체로부터 그것을 삽입 하십시오 :
type ResourceFormatter struct {}
func(r *ResourceFormatter) FormatHTTP(resource string) string {
return fmt.Sprintf("http://%s", resource)
}
func(r *ResourceFormatter) FormatHTTPS(resource string) string {
return fmt.Sprintf("https://%s", resource)
}
type AuthenticatedRequest struct {
Request
Username, Password string
ResourceFormatter
}
이제 주요 기능은 다음을 수행 할 수 있습니다.
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "www.example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
println(ar.FormatHTTP(ar.Resource))
println(ar.FormatHTTPS(ar.Resource))
fmt.Printf("%#v", ar)
}
ResourceFormatter
내장 된 AuthenticatedRequest
가 있는지 확인하십시오.
그러나 단점은 컴포지션 외부의 개체에 액세스 할 수 없다는 것입니다. 따라서 ResourceFormatter
는 AuthenticatedRequest
멤버에 액세스 할 수 없습니다.
행동 양식
구조체 메소드는 함수와 매우 유사합니다.
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
유일한 차이점은 메서드 수신기가 추가 된 것입니다. 이것은 유형의 인스턴스 또는 유형의 인스턴스에 대한 포인터로 선언 될 수 있습니다. SetName()
은 인스턴스를 변경하기 때문에 수신자는 인스턴스에서 영구적 인 변경을 수행하기 위해 포인터 여야합니다.
예 :
package main
import "fmt"
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
func main() {
var me User
me.SetName("Slim Shady")
fmt.Println("My name is", me.Name())
}
익명 구조체
익명 구조체를 생성하는 것이 가능합니다 :
data := struct {
Number int
Text string
} {
42,
"Hello world!",
}
전체 예제 :
package main
import (
"fmt"
)
func main() {
data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
fmt.Printf("%+v\n", data)
}
태그
구조체 필드에는 태그가 연결될 수 있습니다. 이러한 태그는 reflect
패키지에서 읽을 수 있으므로 개발자가 필드에 대해 지정한 사용자 정의 정보를 얻을 수 있습니다.
struct Account {
Username string `json:"username"`
DisplayName string `json:"display_name"`
FavoriteColor string `json:"favorite_color,omitempty"`
}
위의 예에서 태그는 JSON을 마샬링하거나 언 마샬링 할 때 encoding/json
패키지에서 사용하는 키 이름을 변경하는 데 사용됩니다.
태그는 임의의 문자열 값이 될 수 있지만 공백으로 구분 된 key:"value"
쌍을 사용하는 것이 가장 좋습니다.
struct StructName {
FieldName int `package1:"customdata,moredata" package2:"info"`
}
encoding/xml
및 encoding/json
패키지와 함께 사용되는 struct 태그는 표준 libarary에서 사용됩니다.
struct 복사본 만들기.
struct는 할당을 사용하여 간단히 복사 할 수 있습니다.
type T struct {
I int
S string
}
// initialize a struct
t := T{1, "one"}
// make struct copy
u := t // u has its field values equal to t
if u == t { // true
fmt.Println("u and t are equal") // Prints: "u and t are equal"
}
위의 경우 't'
와 'u'는 이제 별도의 객체 (구조체 값)입니다.
T
는 필드로 참조 유형 (슬라이스, 맵, 채널)을 포함하지 않으므로 위의 t
및 u
는 서로 영향을주지 않고 수정할 수 있습니다.
fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1
그러나 T
에 참조 유형이 포함 된 경우 (예 :
type T struct {
I int
S string
xs []int // a slice is a reference type
}
그런 다음 할당에 의한 간단한 복사가 슬라이스 유형 필드의 값을 새 객체에 복사합니다. 동일한 슬라이스 객체를 참조하는 두 개의 서로 다른 객체가 생성됩니다.
// initialize a struct
t := T{I: 1, S: "one", xs: []int{1, 2, 3}}
// make struct copy
u := t // u has its field values equal to t
u와 t가 모두 자신의 필드 xs를 통해 동일한 슬라이스를 참조하기 때문에 한 객체의 슬라이스에서 값을 업데이트하면 다른 객체의 슬라이스에 변경 사항이 반영됩니다.
// update a slice field in u
u.xs[1] = 500
fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 500 3], u.xs = [1 500 3]
따라서이 참조 유형 속성이 의도하지 않은 동작을 생성하지 않도록하려면주의해야합니다.
예를 들어 위의 객체를 복사하려면 슬라이스 필드의 명시적인 사본을 수행 할 수 있습니다.
// explicitly initialize u's slice field
u.xs = make([]int, len(t.xs))
// copy the slice values over from t
copy(u.xs, t.xs)
// updating slice value in u will not affect t
u.xs[1] = 500
fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 2 3], u.xs = [1 500 3]
구조체 리터럴
struct 유형의 값은 해당 필드의 값을 지정하는 struct 리터럴 을 사용하여 작성할 수 있습니다.
type Point struct { X, Y int }
p := Point{1, 2}
위의 예는 모든 필드를 올바른 순서로 지정합니다. 프로그래머가 정확한 필드를 순서대로 기억해야하기 때문에 유용하지 않습니다. 종종 필드 이름과 해당 값의 일부 또는 전체를 나열하여 구조체를 초기화 할 수 있습니다.
anim := gif.GIF{LoopCount: nframes}
생략 된 필드는 해당 유형의 0 값으로 설정됩니다.
주 : 두 개의 형식은 동일한 리터럴에서 혼합 될 수 없습니다.
빈 구조체
구조체는 필드라고하는 명명 된 요소의 시퀀스이며 각 필드에는 이름과 유형이 있습니다. 빈 구조체에는 다음과 같은 익명의 빈 구조체와 같은 필드가 없습니다.
var s struct{}
또는 다음과 같이 빈 구조체 유형을 지정합니다.
type T struct{}
빈 구조체에 대한 흥미로운 점은 그 크기가 0입니다 ( Go The Playground 시도).
fmt.Println(unsafe.Sizeof(s))
0
출력하므로 빈 구조체 자체는 메모리를 사용하지 않습니다. 그래서 채널을 끝내기위한 좋은 옵션입니다 ( The Playground을 시도하십시오) :
package main
import (
"fmt"
"time"
)
func main() {
done := make(chan struct{})
go func() {
time.Sleep(1 * time.Second)
close(done)
}()
fmt.Println("Wait...")
<-done
fmt.Println("done.")
}