จะตรวจสอบโครงสร้างว่างได้อย่างไร?


112

ฉันกำหนดโครงสร้าง ...

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

บางครั้งฉันกำหนดเซสชันว่างให้กับมัน (เพราะไม่มีศูนย์)

session = Session{};

จากนั้นฉันต้องการตรวจสอบว่าว่างเปล่า:

if session == Session{} {
     // do stuff...
}

เห็นได้ชัดว่านี่ใช้งานไม่ได้ ฉันจะเขียนมันได้อย่างไร?


4
เซสชัน {} ไม่ใช่เซสชัน "ว่าง" เริ่มต้นโดยแต่ละฟิลด์เป็นค่าศูนย์
Paul Hankin

คำตอบ:


181

คุณสามารถใช้ == เพื่อเปรียบเทียบกับค่าคอมโพสิตลิเทอรัลที่มีค่าเป็นศูนย์เนื่องจากทุกฟิลด์สามารถเปรียบเทียบได้ :

if (Session{}) == session  {
    fmt.Println("is zero value")
}

ตัวอย่างสนามเด็กเล่น

เนื่องจากความคลุมเครือในการแยกวิเคราะห์จึงจำเป็นต้องใช้วงเล็บรอบตัวอักษรผสมในเงื่อนไข if

การใช้==ข้างต้นใช้กับโครงสร้างที่ทุกฟิลด์สามารถเทียบเคียงได้ หากโครงสร้างมีเขตข้อมูลที่ไม่สามารถเทียบเคียงกันได้ (สไลซ์แผนที่หรือฟังก์ชัน) ฟิลด์นั้นจะต้องถูกเปรียบเทียบทีละช่องกับค่าศูนย์

อีกทางเลือกหนึ่งในการเปรียบเทียบค่าทั้งหมดคือการเปรียบเทียบเขตข้อมูลที่ต้องตั้งค่าเป็นค่าที่ไม่ใช่ศูนย์ในเซสชันที่ถูกต้อง ตัวอย่างเช่นหากรหัสผู้เล่นต้องเป็น! = "" ในเซสชันที่ถูกต้องให้ใช้

if session.playerId == "" {
    fmt.Println("is zero value")
}

4
@kristen Dereference ตัวชี้และเปรียบเทียบ ถ้าsessionเป็นที่ไม่ใช่ศูนย์ใช้แล้ว*Session if (Session{} == *session {
Muffin Top

3
ดังนั้นฉันจึงได้รับข้อผิดพลาดstruct containing []byte cannot be comparedเพราะโครงสร้างของฉันมีชิ้นส่วนไบต์
Nevermore

16
@ อีกต่อไปคำตอบใช้กับโครงสร้างที่มีเขตข้อมูลที่เปรียบเทียบได้ หากโครงสร้างของคุณมีค่าที่เทียบเคียงไม่ได้เช่น [] ไบต์คุณจะต้องเขียนโค้ดเพื่อทดสอบฟิลด์ทั้งหมดหรือใช้แพ็กเกจสะท้อนตามที่ระบุไว้ในคำตอบอื่น
Muffin Top

2
ตามที่ @Nevermore กล่าวไว้==การเปรียบเทียบกับช่อง slice จะล้มเหลว สำหรับการเปรียบเทียบโครงสร้างเหล่านี้ให้ใช้อย่างใดอย่างหนึ่งreflect.DeepEqualหรือพิจารณาสิ่งที่เชี่ยวชาญกว่าเช่นที่กล่าวถึงที่นี่: stackoverflow.com/questions/24534072/…
asgaines

"parsing ambiguity in [if condition]" บันทึกวันของฉันไว้ขอบคุณ :) เพราะเมื่อฉันพยายามใน fmt.Println (เซสชัน == เซสชัน {}) ก็ใช้ได้
Franva

38

คำแนะนำหรือเทคนิคเพิ่มเติม 3 ประการ:

ด้วยฟิลด์เพิ่มเติม

คุณสามารถเพิ่มฟิลด์เพิ่มเติมเพื่อบอกว่าโครงสร้างถูกเติมหรือว่างเปล่า ฉันตั้งใจตั้งชื่อมันreadyไม่ใช่emptyเพราะค่าศูนย์ของ a boolคือfalseดังนั้นหากคุณสร้างโครงสร้างใหม่เหมือนฟิลด์Session{}ของมันreadyจะเป็นไปโดยอัตโนมัติfalseและจะบอกความจริงกับคุณว่าโครงสร้างยังไม่พร้อม (มันว่างเปล่า)

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

เมื่อคุณเริ่มต้นโครงสร้างคุณต้องตั้งค่าreadyเป็นtrue. isEmpty()วิธีการของคุณไม่จำเป็นอีกต่อไป (แม้ว่าคุณจะสามารถสร้างได้หากต้องการ) เพราะคุณสามารถทดสอบreadyฟิลด์ได้เอง

var s Session

if !s.ready {
    // do stuff (populate s)
}

ความสำคัญของboolฟิลด์เพิ่มเติมหนึ่งฟิลด์นี้จะเพิ่มขึ้นเมื่อโครงสร้างขยายใหญ่ขึ้นหรือหากมีฟิลด์ที่เทียบไม่ได้ (เช่นสไลซ์mapและค่าฟังก์ชัน)

การใช้ค่าศูนย์ของฟิลด์ที่มีอยู่

สิ่งนี้คล้ายกับคำแนะนำก่อนหน้านี้ แต่จะใช้ค่าศูนย์ของฟิลด์ที่มีอยู่ซึ่งถือว่าไม่ถูกต้องเมื่อโครงสร้างไม่ว่างเปล่า การใช้งานนี้ขึ้นอยู่กับการใช้งาน

ตัวอย่างเช่นหากในตัวอย่างของคุณคุณplayerIdไม่สามารถเป็นค่าว่างstring ""คุณสามารถใช้เพื่อทดสอบว่าโครงสร้างของคุณว่างเปล่าเช่นนี้:

var s Session

if s.playerId == "" {
    // do stuff (populate s, give proper value to playerId)
}

ในกรณีนี้ควรรวมเช็คนี้ไว้ในisEmpty()วิธีการหนึ่งเนื่องจากการตรวจสอบนี้ขึ้นอยู่กับการใช้งาน:

func (s Session) isEmpty() bool {
    return s.playerId == ""
}

และใช้มัน:

if s.isEmpty() {
    // do stuff (populate s, give proper value to playerId)
}

ใช้ตัวชี้ไปที่โครงสร้างของคุณ

ข้อเสนอแนะที่สองคือการใช้ตัวชี้ไปยัง struct *Sessionของคุณ: พอยน์เตอร์สามารถมีnilค่าได้ดังนั้นคุณสามารถทดสอบได้:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}

คำตอบที่ดี ขอบคุณ icza!
Evgeny Goldin

คำตอบสุดเจ๋ง! ฉันคิดว่าการทำตามตัวเลือกสุดท้ายดูเป็นสำนวนที่ดี
DeivinsonTejeda

20

การใช้reflect.deepEqualก็ใช้ได้เช่นกันโดยเฉพาะอย่างยิ่งเมื่อคุณมีแผนที่ภายในโครงสร้าง

package main

import "fmt"
import "time"
import "reflect"

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

func (s Session) IsEmpty() bool {
  return reflect.DeepEqual(s,Session{})
}

func main() {
  x := Session{}
  if x.IsEmpty() {
    fmt.Print("is empty")
  } 
}

2
การใช้ reflect.DeepEqual เป็นโซลูชันที่สะอาดมาก แต่ฉันสงสัยว่าต้องใช้เวลาในการประมวลผลมากกว่านี้หรือไม่? ฉันคิดว่ามันกำลังเปรียบเทียบทุกฟิลด์บวกกับคุณแนะนำการนำเข้าใหม่
ฤ.

5

โปรดทราบว่าเมื่อใช้พอยน์เตอร์ถึงโครงสร้างคุณจะต้องหักล้างตัวแปรและอย่าเปรียบเทียบกับตัวชี้เป็นโครงสร้างว่าง:

session := &Session{}
if (Session{}) == *session {
    fmt.Println("session is empty")
}

ตรวจสอบสนามเด็กเล่นนี้

นอกจากนี้คุณจะเห็นว่าโครงสร้างที่ถือคุณสมบัติซึ่งเป็นส่วนของพอยน์เตอร์ไม่สามารถเปรียบเทียบได้ในลักษณะเดียวกัน ...


1

เพียงแค่เพิ่มเติมอย่างรวดเร็วเพราะฉันได้แก้ไขปัญหาเดียวกันในวันนี้:

ด้วย Go 1.13 คุณสามารถใช้isZero()วิธีการใหม่:

if reflect.ValueOf(session).IsZero() {
     // do stuff...
}

ผมไม่ได้ทดสอบนี้ผลการดำเนินงานเกี่ยวกับการ reflect.DeepEqual()แต่ฉันเดาว่าเรื่องนี้ควรจะเร็วกว่าเมื่อเทียบผ่าน


0

เป็นอีกทางเลือกหนึ่งของคำตอบอื่น ๆ คุณสามารถทำได้ด้วยไวยากรณ์ที่คล้ายกับที่คุณตั้งใจไว้ในตอนแรกหากคุณทำผ่านcaseคำสั่งแทนที่จะเป็นif:

session := Session{}
switch {
case Session{} == session:
    fmt.Println("zero")
default:
    fmt.Println("not zero")
}

ตัวอย่างสนามเด็กเล่น


-1

อาจจะเป็นแบบนี้

package main

import "fmt"
import "time"

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

func (s Session) Equal(o Session) bool {
   if(s.playerId != o.playerId) { return false }
   if(s.beehive != o.beehive) { return false }
   if(s.timestamp != o.timestamp) { return false }
   return true
}

func (s Session) IsEmpty() bool {
    return s.Equal(Session{})
}

func main() {
    x := Session{}
    if x.IsEmpty() {
       fmt.Print("is empty")
    } 
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.