Menu

Golang Enum: const ve iota ile Enum Oluşturmak

Go'da enum anahtar kelimesi yoktur. Bu sayfa deyimsel karşılığı gösteriyor: adlandırılmış bir tip artı iota'lı bir const bloğu; ayrıca String(), doğrulama, ayrıştırma, bit bayrakları ve JSON desteğinin nasıl ekleneceği.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Go'da enum anahtar kelimesi yoktur. Bir enum'u iki parçadan kurarsınız: adlandırılmış bir tip ve o tipteki değerlerden oluşan, iota ile numaralandırılmış bir const bloğu.

Sunday 0'dır ve takip eden her satır bir fazlasıdır. Bunu bir sayı listesi değil de bir enum yapan, adlandırılmış Weekday tipidir: isWeekend imzasında ne beklediğini söyler ve tipe metotlar eklenebilir. Çıktı 1 5 6 yazdırır, çünkü henüz hiçbir şey Go'ya bir Weekday'i metin olarak nasıl göstereceğini söylemiyor. O aşağıda geliyor.

iota Nasıl Çalışır

iota, derleyicinin bir const bloğu içinde sağladığı bir sayaçtır. İki kural onun üzerine kurulan her hileyi açıklar:

  1. iota, bloktaki geçerli satırın 0'dan başlayan indeksine eşittir ve her yeni const bloğunda 0'a sıfırlanır.
  2. = expression içermeyen bir satır, önceki satırın ifadesini ve tipini yeni iota ile değerlendirerek tekrarlar.

Yani yukarıdaki Monday, iota artık 1 iken Monday Weekday = iota'nın kısaltmasıdır. İfade tekrarlandığı için yalnızca iota değil, herhangi bir sabit ifade olabilir:

iota adları değil satırları sayar: X ve Y'nin gösterdiği gibi aynı satırdaki iki sabit tek bir iota değerini paylaşır.

1'den Başlamak ve Neden Başlamayabilirsiniz

Kimsenin ayarlamadığı bir enum tipi değişkeni sıfır değeri olan 0'ı tutar. 0, Sunday gibi gerçek bir değerse "kullanıcı Pazar'ı seçti" ile "alan hiç doldurulmadı"yı ayırt edemezsiniz. Üç yaygın çözüm:

Production kodunda en yaygını 1. seçenektir ve protobuf'un ürettiği Go enum'ları da onu izler (..._UNSPECIFIED = 0). Sıfır değer böylece dürüst bir şey ifade eder.

Değer Atlamak

Boş tanımlayıcı _, bir ad oluşturmadan bir iota değerini tüketir. Boşluk bırakmak için kullanın; örneğin bir protokolün tanımladığı sayılarla eşleşmek ya da geri kalanı yeniden numaralandırmadan bir değeri emekliye ayırmak için:

type Opcode byte

const (
	OpContinue Opcode = iota // 0
	OpText                   // 1
	OpBinary                 // 2
	_                        // 3, reserved
	_                        // 4, reserved
	_                        // 5, reserved
	_                        // 6, reserved
	_                        // 7, reserved
	OpClose                  // 8
	OpPing                   // 9
	OpPong                   // 10
)

Bu WebSocket opcode'larında olduğu gibi sayılar harici bir spesifikasyonla sabitlendiğinde, onları açıkça yazmak (OpClose Opcode = 8) çoğu zaman boşlukları saymaktan daha anlaşılırdır. iota, tam sayılarını umursamadığınız değerler içindir.

Sayıları bir veritabanında, bir dosyada saklanan ya da ağ üzerinden gönderilen bir iota listesinin sırasını asla değiştirmeyin ve ortasına ekleme yapmayın. Ortaya bir satır eklemek ondan sonraki her değeri kaydırır. Yeni değerleri sona ekleyin ya da sayıları açıkça atayın.

String Metodu Eklemek

Tipe bir String() string metodu verin, fmt onu %v, %s ve Println için kullanır:

O metottaki iki ayrıntı önemlidir:

  • Sınır kontrolü. O olmadan Weekday(9).String() index out of range ile panic olur ve bu eninde sonunda olur, çünkü hiçbir şey bir çağıranın Weekday(9) oluşturmasını engellemez.
  • Sprintf içindeki int(d). d'nin kendisini %d ile biçimlendirmek sorun değildir, ama %v ile biçimlendirmek String()'i yeniden çağırır ve stack taşana kadar özyinelemeye girer.

%d yine sayıyı yazdırır, bu yüzden iki biçimi de elde edersiniz: Wednesday is day 3.

String'i stringer ile üretmek

Uzun listeler için stringer aracı metodu sizin yerinize yazar:

//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=Weekday
go generate ./...

Kompakt bir String() gerçekleştirmesi içeren weekday_string.go dosyasını oluşturur; ayrıca sabitler yeniden üretilmeden değişirse derlemeyi bozan bir derleme zamanı kontrolü ekler. -linecomment bayrağı sondaki bir yorumu ad olarak kullanır; bu, boşluk içeren adlar için kullanışlıdır.

Değerleri Doğrulamak

Bir Go enum'u kapalı değildir. Temel tipin herhangi bir değeri ona dönüşür ve tipsiz sabitler örtük olarak dönüşür:

var d Weekday = 42     // compiles
d = Weekday(userInput) // compiles

Bu yüzden kodunuzun dışından gelen değerleri (JSON, veritabanları, bayraklar, diğer paketler) kontrol edin:

Bloğun sonundaki dışa kapalı colorCount işaretçisi, yeni renkler eklediğinizde IsValid'i doğru tutar, çünkü her zaman son gerçek değerin bir fazlasında durur.

Bir Enum Üzerinde switch

Enum'lar genellikle bir switch tarafından tüketilir. Go bir switch'in her değeri kapsayıp kapsamadığını kontrol etmez, bu yüzden sürprizi bildiren bir default ekleyin:

func (c Color) Hex() string {
	switch c {
	case Red:
		return "#ff0000"
	case Green:
		return "#00ff00"
	case Blue:
		return "#0000ff"
	default:
		return "#000000"
	}
}

Üçüncü taraf exhaustive linter'ı (golangci-lint'e dahildir), enum tipleri üzerinde bir case'i atlayan switch'leri raporlar; bu da size diğer dillerdeki kapsayıcı enum kontrolünün sağladıklarının çoğunu verir.

Bit Bayrağı Enum'ları

İzinler gibi değerler birleşiyorsa, 1 << iota ile her değer için bir bit kullanın:

| bayrakları birleştirir, & onları test eder, &^ (Go'nun AND NOT operatörü) onları temizler. Burada doğru seçim işaretsiz bir temel tiptir: uint8 8 bayrak, uint64 64 bayrak tutar.

String Enum'ları

Değer zaten metin olarak saklanıyor ya da gönderiliyorsa, string tabanlı bir tip dönüşüm katmanından kaçınır:

type Env string

const (
	EnvDev     Env = "dev"
	EnvStaging Env = "staging"
	EnvProd    Env = "prod"
)

Değerler String() metodu olmadan okunabilir biçimde yazdırılır ve serileştirilir; bir veritabanı sütunu, tanım sırasına bağlı bir sayı yerine "prod" tutar. Ödünleşimler: karşılaştırmalar string karşılaştırmalarıdır, bit bayrakları imkânsızdır ve doğrulama yine size kalır, çünkü Env("banana") da derlenir.

Enum'lar ve JSON

Bir tam sayı enum'u varsayılan olarak bir sayı olarak serileştirilir. Bunun yerine adları okuyup yazmak için encoding.TextMarshaler ve encoding.TextUnmarshaler'ı gerçekleştirin. encoding/json bunları değerler ve map anahtarları için kullanır:

MarshalText'in değer alıcısı vardır, böylece hem Level hem de *Level üzerinde çalışır; UnmarshalText değeri değiştirdiği için pointer alıcıya ihtiyaç duyar. Aynı iki metot tipin flag paketinin TextVar'ı ve çoğu yapılandırma kütüphanesiyle çalışmasını da sağlar.

Tuzaklar

  • Literal'lerin örtük dönüşümü. Bir Weekday alan fonksiyon tipsiz sabit 42'yi de kabul eder. Yalnızca başka bir tipin tipli değerleri reddedilir.
  • İlk satırda tipi unutmak. const ( Red = iota; Green; Blue ) içinde üçü de Color değeri değil, tipsiz tam sayı sabitleridir, bu yüzden Color üzerindeki metotlar onlara uygulanmaz. Tekrarlanan ifadenin tipi taşıması için Red Color = iota yazın.
  • Saklanan enum'ların sırasını değiştirmek. Bir iota bloğunun ortasına bir değer eklemek, başka yerlerde zaten kaydedilmiş sayıları sessizce değiştirir.
  • String içinde özyineleme. String() içinde alıcıyı asla %v ya da %s ile biçimlendirmeyin. Önce temel tipe dönüştürün.

Sıkça Sorulan Sorular

Go'da enum var mı?

Dil özelliği olarak yok. enum anahtar kelimesi yoktur. Deyimsel karşılığı, adlandırılmış bir tip artı genellikle iota ile numaralandırılan tipli sabitlerden oluşan bir bloktur:

type Color int

const (
	Red Color = iota
	Green
	Blue
)

Tip size okunabilir imzalar ve String() gibi metotları ekleyecek bir yer verir. Birinin Color(42) yazmasını engellemez, bu yüzden dışarıdan gelen değerleri doğrulayın.

Go'da iota nedir?

iota, bir const bloğu içinde 0'dan başlayarak geçerli satırın (sabit tanımının) indeksine eşit olan önceden tanımlı bir tanımlayıcıdır. Her yeni const bloğunda 0'a sıfırlanır. Bir satır ifadesini yazmadığında Go önceki ifadeyi bir sonraki iota ile tekrarlar; Red = iota; Green; Blue ifadesinin 0, 1, 2 üretmesini sağlayan budur.

iota 1'den nasıl başlatılır?

İlk satıra First Kind = iota + 1 yazın ya da sıfırı boş tanımlayıcıyla atlayın: önce _ = iota, sonra First. Birçok Go programcısı bunun yerine 0'ı tutup ona Unknown ya da Invalid adını verir; böylece başlatılmamış bir değişken (sıfır değeri 0'dır) açıkça gerçek bir seçim olmaz.

Go'da bir enum string olarak nasıl yazdırılır?

Tipe bir String() string metodu verin. fmt onu %v, %s ve Println için çağırır, bu yüzden fmt.Println(Green), 1 yerine Green yazdırır. Metodu bir switch ya da bir dizi ile elle yazabilir veya go run golang.org/x/tools/cmd/stringer@latest -type=Color ile üretebilirsiniz.

Go'da bir string nasıl enum'a dönüştürülür?

String'i genellikle bir map[string]Color ya da bir switch içinde arayan ve bilinmeyen girdi için hata döndüren bir ayrıştırma fonksiyonu yazın: func ParseColor(s string) (Color, error). Aynı mantıkla UnmarshalText'i gerçekleştirmek, JSON'un, bayrakların ve yapılandırma yükleyicilerinin onu otomatik olarak kullanmasını sağlar.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA