781 Stimmen

Was ist eine idiomatische Art der Darstellung von Enums in Go?

Ich versuche, ein vereinfachtes Chromosom darzustellen, das aus N Basen besteht, von denen jede nur eine der folgenden sein kann {A, C, T, G} .

Ich möchte die Einschränkungen mit einem enum formalisieren, aber ich frage mich, was die meisten idiomatischen Weg der Emulation eines enum in Go ist.

905voto

zzzz Punkte 83651

Ich zitiere aus den Sprachspezifikationen: Iota

Innerhalb einer Konstantendeklaration steht der vordeklarierte Bezeichner iota für aufeinanderfolgende nicht typisierte Integer-Konstanten. Er wird auf 0 zurückgesetzt, wenn das reservierte Wort const im Quelltext erscheint, und wird nach jeder ConstSpec erhöht. Er kann verwendet werden, um eine Reihe von zusammenhängenden Konstanten zu konstruieren:

const (  // iota is reset to 0
        c0 = iota  // c0 == 0
        c1 = iota  // c1 == 1
        c2 = iota  // c2 == 2
)

const (
        a = 1 << iota  // a == 1 (iota has been reset)
        b = 1 << iota  // b == 2
        c = 1 << iota  // c == 4
)

const (
        u         = iota * 42  // u == 0     (untyped integer constant)
        v float64 = iota * 42  // v == 42.0  (float64 constant)
        w         = iota * 42  // w == 84    (untyped integer constant)
)

const x = iota  // x == 0 (iota has been reset)
const y = iota  // y == 0 (iota has been reset)

Innerhalb einer ExpressionList ist der Wert jedes Jota gleich, da er nur nach jedem ConstSpec inkrementiert wird:

const (
        bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0
        bit1, mask1                           // bit1 == 2, mask1 == 1
        _, _                                  // skips iota == 2
        bit3, mask3                           // bit3 == 8, mask3 == 7
)

In diesem letzten Beispiel wird die implizite Wiederholung der letzten nicht leeren Ausdrucksliste ausgenutzt.


Ihr Code könnte also wie folgt aussehen

const (
        A = iota
        C
        T
        G
)

oder

type Base int

const (
        A Base = iota
        C
        T
        G
)

wenn Sie wollen, dass Basen ein anderer Typ als int ist.

121voto

metakeule Punkte 3614

In Anlehnung an die Antwort von jnml könnten Sie neue Instanzen des Typs Base verhindern, indem Sie den Typ Base überhaupt nicht exportieren (d.h. klein schreiben). Bei Bedarf können Sie eine exportierbare Schnittstelle erstellen, die eine Methode hat, die einen Basistyp zurückgibt. Diese Schnittstelle könnte in Funktionen von außen verwendet werden, die mit Bases arbeiten, z.B.

package a

type base int

const (
    A base = iota
    C
    T
    G
)

type Baser interface {
    Base() base
}

// every base must fulfill the Baser interface
func(b base) Base() base {
    return b
}

func(b base) OtherMethod()  {
}

package main

import "a"

// func from the outside that handles a.base via a.Baser
// since a.base is not exported, only exported bases that are created within package a may be used, like a.A, a.C, a.T. and a.G
func HandleBasers(b a.Baser) {
    base := b.Base()
    base.OtherMethod()
}

// func from the outside that returns a.A or a.C, depending of condition
func AorC(condition bool) a.Baser {
    if condition {
       return a.A
    }
    return a.C
}

Im Inneren des Hauptpakets a.Baser ist jetzt effektiv wie eine Aufzählung. Nur innerhalb eines Pakets können Sie neue Instanzen definieren.

73voto

Azat Punkte 908

Sie können es so machen:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

Mit diesem Code sollte der Compiler den Typ der Aufzählung prüfen

67voto

Becca Petrin Punkte 1413

Es stimmt, dass die oben genannten Beispiele für die Verwendung von const y iota sind die idiomatischsten Möglichkeiten, primitive Enums in Go darzustellen. Aber was ist, wenn Sie nach einer Möglichkeit suchen, eine Enum mit mehr Funktionen zu erstellen, ähnlich dem Typ, den Sie in einer anderen Sprache wie Java oder Python sehen würden?

Ein sehr einfacher Weg, um ein Objekt zu erstellen, das wie ein String-Enum in Python aussieht und sich anfühlt, wäre folgender:

package main

import (
    "fmt"
)

var Colors = newColorRegistry()

func newColorRegistry() *colorRegistry {
    return &colorRegistry{
        Red:   "red",
        Green: "green",
        Blue:  "blue",
    }
}

type colorRegistry struct {
    Red   string
    Green string
    Blue  string
}

func main() {
    fmt.Println(Colors.Red)
}

Nehmen wir an, Sie wollten auch einige Hilfsmethoden, wie Colors.List() y Colors.Parse("red") . Und Ihre Farben waren komplexer und mussten eine Struktur sein. Dann könnten Sie etwas in der Art machen:

package main

import (
    "errors"
    "fmt"
)

var Colors = newColorRegistry()

type Color struct {
    StringRepresentation string
    Hex                  string
}

func (c *Color) String() string {
    return c.StringRepresentation
}

func newColorRegistry() *colorRegistry {

    red := &Color{"red", "F00"}
    green := &Color{"green", "0F0"}
    blue := &Color{"blue", "00F"}

    return &colorRegistry{
        Red:    red,
        Green:  green,
        Blue:   blue,
        colors: []*Color{red, green, blue},
    }
}

type colorRegistry struct {
    Red   *Color
    Green *Color
    Blue  *Color

    colors []*Color
}

func (c *colorRegistry) List() []*Color {
    return c.colors
}

func (c *colorRegistry) Parse(s string) (*Color, error) {
    for _, color := range c.List() {
        if color.String() == s {
            return color, nil
        }
    }
    return nil, errors.New("couldn't find it")
}

func main() {
    fmt.Printf("%s\n", Colors.List())
}

Dann funktioniert es zwar, aber es gefällt Ihnen vielleicht nicht, dass Sie die Farben immer wieder neu definieren müssen. Wenn Sie das vermeiden möchten, könnten Sie Tags für Ihre Struktur verwenden und einige ausgefallene Überlegungen anstellen, um sie einzurichten, aber ich hoffe, dass dies für die meisten Menschen ausreicht.

43voto

Yu Huang Punkte 2505

Es gibt eine Möglichkeit mit struct namespace.

Der Vorteil ist, dass alle Enum-Variablen in einem bestimmten Namensraum liegen, um Verschmutzung zu vermeiden. Das Problem ist, dass wir nur var no const

type OrderStatusType string

var OrderStatus = struct {
    APPROVED         OrderStatusType
    APPROVAL_PENDING OrderStatusType
    REJECTED         OrderStatusType
    REVISION_PENDING OrderStatusType
}{
    APPROVED:         "approved",
    APPROVAL_PENDING: "approval pending",
    REJECTED:         "rejected",
    REVISION_PENDING: "revision pending",
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X