Wie implementiere ich Enums in Ruby?

323

Was ist der beste Weg, um die Enum-Sprache in Ruby zu implementieren? Ich suche etwas, das ich (fast) wie die Java / C # -Aufzählungen verwenden kann.

Auramo
quelle
7
@auramo, gute Frage und gute Wahl für die beste Antwort. Liebe es oder hasse es, du bekommst keine Typensicherheit und (zumindest in Ruby) keine Tippfehler. Ich war begeistert, als ich Enums in C # und später in Java entdeckte (wählen Sie einen Wert aus, aber aus diesen!). Ruby bietet auf keinen Fall eine echte Möglichkeit, dies zu tun.
Dan Rosenstark
2
Das Problem bei dieser Frage ist, dass Java- und C # -Aufzählungen dramatisch unterschiedliche Dinge sind. Ein Java-Enum-Member ist eine Objektinstanz und ein Singleton. Eine Java-Enumeration kann einen Konstruktor haben. Im Gegensatz dazu basieren C # -Aufzählungen auf primitiven Werten. Welches Verhalten sucht der Fragesteller? Während es wahrscheinlich der Fall ist, dass der C # -Fall gewünscht wird, wird Java explizit erwähnt und nicht C oder C ++, daher gibt es einige Zweifel. Der Hinweis, dass es in Ruby keine Möglichkeit gibt, "sicher" zu sein, ist transparent falsch, aber Sie müssen etwas Anspruchsvolleres implementieren.
user1164178

Antworten:

317

Zwei Wege. Symbole ( :fooNotation) oder Konstanten ( FOONotation).

Symbole sind geeignet, wenn Sie die Lesbarkeit verbessern möchten, ohne Code mit Literalzeichenfolgen zu verunreinigen.

postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"

Konstanten sind angemessen, wenn Sie einen wichtigen zugrunde liegenden Wert haben. Deklarieren Sie einfach ein Modul, das Ihre Konstanten enthält, und deklarieren Sie dann die darin enthaltenen Konstanten.

module Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end

flags = Foo::BAR | Foo::BAZ # flags = 3
mlibby
quelle
2
Was ist, wenn diese Aufzählung auch in der Datenbank gespeichert wird? Funktioniert die Symbolnotation? Ich bezweifle ...
Phương Nguyễn
Ich würde den Konstantenansatz verwenden, wenn ich in einer Datenbank speichern würde. Natürlich müssen Sie dann eine Art Suche durchführen, wenn Sie die Daten wieder aus der Datenbank ziehen. Sie können auch etwas wie :minnesota.to_sbeim Speichern in einer Datenbank verwenden, um die Zeichenfolgenversion des Symbols zu speichern. Ich glaube, Rails hat einige Hilfsmethoden, um mit einigen davon umzugehen.
mlibby
7
Wäre ein Modul nicht besser, um Konstanten zu gruppieren - da Sie keine Instanzen davon erstellen werden?
Thomthom
3
Nur ein Kommentar. Ruby ist ein bisschen nervig, wenn es darum geht, Konventionen zu benennen, aber nicht wirklich offensichtlich, bis Sie darüber stolpern. Die Namen der Aufzählungen müssen alle Großbuchstaben sein und der erste Buchstabe des Modulnamens muss groß geschrieben werden, damit Ruby weiß, dass das Modul ein Modul von Konstanten ist.
Rokujolady
3
Nicht ganz richtig. Der erste Buchstabe der Konstante muss groß geschrieben werden, aber nicht alle Buchstaben müssen groß geschrieben werden. Dies ist eine Frage der Konventionspräferenz. Beispielsweise sind alle Modul- und Klassennamen auch Konstanten.
Michael Brown
59

Ich bin überrascht, dass niemand so etwas angeboten hat (geerntet aus dem RAPI- Juwel):

class Enum

  private

  def self.enum_attr(name, num)
    name = name.to_s

    define_method(name + '?') do
      @attrs & num != 0
    end

    define_method(name + '=') do |set|
      if set
        @attrs |= num
      else
        @attrs &= ~num
      end
    end
  end

  public

  def initialize(attrs = 0)
    @attrs = attrs
  end

  def to_i
    @attrs
  end
end

Welches kann so verwendet werden:

class FileAttributes < Enum
  enum_attr :readonly,       0x0001
  enum_attr :hidden,         0x0002
  enum_attr :system,         0x0004
  enum_attr :directory,      0x0010
  enum_attr :archive,        0x0020
  enum_attr :in_rom,         0x0040
  enum_attr :normal,         0x0080
  enum_attr :temporary,      0x0100
  enum_attr :sparse,         0x0200
  enum_attr :reparse_point,  0x0400
  enum_attr :compressed,     0x0800
  enum_attr :rom_module,     0x2000
end

Beispiel:

>> example = FileAttributes.new(3)
=> #<FileAttributes:0x629d90 @attrs=3>
>> example.readonly?
=> true
>> example.hidden?
=> true
>> example.system?
=> false
>> example.system = true
=> true
>> example.system?
=> true
>> example.to_i
=> 7

Dies funktioniert gut in Datenbankszenarien oder beim Umgang mit Konstanten / Aufzählungen im C-Stil (wie dies bei der Verwendung von FFI der Fall ist , das RAPI in großem Umfang verwendet).

Außerdem müssen Sie sich keine Sorgen über Tippfehler machen, die stille Fehler verursachen, wie Sie es bei der Verwendung einer Hash-Lösung tun würden.

Charles
quelle
1
Das ist eine großartige Möglichkeit, dieses spezielle Problem zu lösen, aber der Grund, warum niemand vorgeschlagen hat, hat wahrscheinlich damit zu tun, dass es nicht viel mit C # / Java-Enums zu tun hat.
mlibby
1
Dies ist etwas unvollständig, dient aber als guter Hinweis darauf, wie Sie Lösungen mit einem dynamischen Ansatz implementieren können. Es hat eine gewisse Ähnlichkeit mit einer C # -Aufzählung mit dem FlagsAttribute-Satz, aber wie die obigen symbol- / konstantenbasierten Lösungen ist es eine Antwort von vielen. Das Problem ist die ursprüngliche Frage, die in ihrer Absicht durcheinander ist (C # und Java sind nicht austauschbar). Es gibt viele Möglichkeiten, Objekte in Ruby aufzulisten. Die Auswahl des richtigen hängt vom zu lösenden Problem ab. Das sklavische Replizieren von Funktionen, die Sie nicht benötigen, ist falsch. Die richtige Antwort muss vom Kontext abhängen.
user1164178
52

Der idiomatischste Weg, dies zu tun, ist die Verwendung von Symbolen. Zum Beispiel anstelle von:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... Sie können nur Symbole verwenden:

# You don't actually need to declare these, of course--this is
# just to show you what symbols look like.
:foo
:bar
:baz

my_func(:foo)

Dies ist etwas offener als Aufzählungen, passt aber gut zum Ruby-Geist.

Symbole funktionieren auch sehr gut. Das Vergleichen von zwei Symbolen auf Gleichheit ist beispielsweise viel schneller als das Vergleichen von zwei Zeichenfolgen.

emk
quelle
107
Der Ruby-Geist lautet also: "Tippfehler werden kompiliert"
mxcl
82
Beliebte Ruby-Frameworks stützen sich stark auf die Metaprogrammierung zur Laufzeit, und eine zu lange Überprüfung der Ladezeit würde Rubys Ausdruckskraft den größten Teil nehmen. Um Probleme zu vermeiden, üben die meisten Ruby-Programmierer testgetriebenes Design, bei dem nicht nur Tippfehler, sondern auch Logikfehler auftreten.
Emk
10
@yar: Nun, Sprachdesign ist eine Reihe von Kompromissen, und Sprachfunktionen interagieren. Wenn Sie eine gute, hochdynamische Sprache wollen, gehen Sie mit Ruby, schreiben Sie zuerst Ihre Unit-Tests und gehen Sie mit dem Geist der Sprache. :-) Wenn Sie nicht danach suchen, gibt es Dutzende anderer hervorragender Sprachen, von denen jede unterschiedliche Kompromisse eingeht.
Emk
10
@emk, ich stimme zu, aber mein persönliches Problem ist, dass ich mich in Ruby sehr wohl fühle, aber ich fühle mich nicht wohl, wenn ich in Ruby umgestalte. Und jetzt, da ich (endlich) angefangen habe, Unit-Tests zu schreiben, stelle ich fest, dass sie kein Allheilmittel sind: Ich vermute, 1) dass Ruby-Code in der Praxis nicht so oft massiv überarbeitet wird und 2) Ruby nicht das Ende ist -of-the-line in Bezug auf dynamische Sprachen, gerade weil es schwierig ist, automatisch umzugestalten. Siehe meine Frage 2317579, die seltsamerweise von den Smalltalk-Leuten übernommen wurde.
Dan Rosenstark
4
Ja, aber die Verwendung dieser Zeichenfolgen wäre nicht im Sinne der C # -Sprache, es ist einfach eine schlechte Praxis.
Ed S.
38

Ich benutze den folgenden Ansatz:

class MyClass
  MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end

Ich mag es für die folgenden Vorteile:

  1. Es gruppiert Werte visuell als Ganzes
  2. Es wird eine Überprüfung der Kompilierungszeit durchgeführt (im Gegensatz zur Verwendung von Symbolen).
  3. Ich kann leicht auf die Liste aller möglichen Werte zugreifen: nur MY_ENUM
  4. Ich kann leicht auf verschiedene Werte zugreifen: MY_VALUE_1
  5. Es kann Werte jeden Typs haben, nicht nur Symbol

Symbole sind möglicherweise besser, da Sie den Namen der äußeren Klasse nicht schreiben müssen, wenn Sie ihn in einer anderen Klasse verwenden ( MyClass::MY_VALUE_1)

Alexey
quelle
4
Ich denke, das ist die beste Antwort. Die Funktionalität, Syntax und der minimale Code-Overhead kommen Java / C # am nächsten. Außerdem können Sie die Definitionen noch tiefer als eine Ebene verschachteln und dennoch alle Werte mit MyClass :: MY_ENUM.flatten wiederherstellen. Als Randnotiz würde ich hier Großbuchstaben verwenden, wie es der Standard für Konstanten in Ruby ist. MyClass :: MyEnum wird möglicherweise mit einem Verweis auf eine Unterklasse verwechselt.
Janosch
@Janosch, ich habe die Namen aktualisiert. danke für den Vorschlag
Alexey
Ich bin immer noch ein wenig verwirrt und der Link 410 (nein, nicht 404). Können Sie Beispiele geben, wie diese Aufzählung verwendet wird?
Shelvacu
17

Wenn Sie Rails 4.2 oder höher verwenden, können Sie Rails-Aufzählungen verwenden.

Rails hat jetzt standardmäßig Aufzählungen, ohne dass Edelsteine ​​eingefügt werden müssen.

Dies ist Java, C ++ - Enums sehr ähnlich (und mehr mit Funktionen).

Zitiert von http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html :

class Conversation < ActiveRecord::Base
  enum status: [ :active, :archived ]
end

# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status  # => "active"

# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status    # => "archived"

# conversation.update! status: 1
conversation.status = "archived"

# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status      # => nil
vedant
quelle
7
Wie Sie sagten - nicht nützlich, wenn das OP keine Rails verwendet (oder genauer gesagt, das Objekt ist nicht vom Typ ActiveRecord). Nur meine Ablehnung zu erklären, ist alles.
Ger
2
Dies sind keine Aufzählungen in Ruby, sondern eine ActiveRecord-Schnittstelle zu Aufzählungen in Ihrer Datenbank. Keine verallgemeinerbare Lösung, die in jedem anderen Anwendungsfall angewendet werden kann.
Adam Lassek
Ich habe das bereits in meiner Antwort erwähnt.
Vedant
Dies ist die beste Antwort IFF mit Rails.
theUtherSide
Ich mag es nicht, weil es in einer Rails-Datenbank gespeichert werden muss (um zu funktionieren) und weil es erlaubt, viele Instanzen der ConversationKlasse zu erstellen - ich glaube, es darf nur eine Instanz zulassen.
Prograils
8

Dies ist meine Herangehensweise an Aufzählungen in Ruby. Ich war kurz und bündig, nicht unbedingt der C-artigste. Irgendwelche Gedanken?

module Kernel
  def enum(values)
    Module.new do |mod|
      values.each_with_index{ |v,i| mod.const_set(v.to_s.capitalize, 2**i) }

      def mod.inspect
        "#{self.name} {#{self.constants.join(', ')}}"
      end
    end
  end
end

States = enum %w(Draft Published Trashed)
=> States {Draft, Published, Trashed} 

States::Draft
=> 1

States::Published
=> 2

States::Trashed
=> 4

States::Draft | States::Trashed
=> 3
johnnypez
quelle
8

Vielleicht wäre der beste leichte Ansatz

module MyConstants
  ABC = Class.new
  DEF = Class.new
  GHI = Class.new
end

Auf diese Weise haben Werte Namen wie in Java / C #:

MyConstants::ABC
=> MyConstants::ABC

Um alle Werte zu erhalten, können Sie dies tun

MyConstants.constants
=> [:ABC, :DEF, :GHI] 

Wenn Sie den Ordnungswert einer Aufzählung möchten, können Sie dies tun

MyConstants.constants.index :GHI
=> 2
Daniel Lubarov
quelle
1
IMHO repliziert dies sehr genau die Verwendung und den Zweck (Typensicherheit) von Java, außerdem können Konstanten vorzugsweise wie class ABC; end
folgt
8

Ich weiß, es ist lange her, dass der Typ diese Frage gestellt hat, aber ich hatte die gleiche Frage und dieser Beitrag gab mir keine Antwort. Ich wollte eine einfache Möglichkeit, um zu sehen, was die Zahl darstellt, einen einfachen Vergleich und vor allem ActiveRecord-Unterstützung für die Suche anhand der Spalte, die die Aufzählung darstellt.

Ich habe nichts gefunden, also habe ich eine großartige Implementierung namens yinum gemacht, die alles erlaubte, was ich suchte. Ich habe eine Menge Spezifikationen erstellt, daher bin ich mir ziemlich sicher, dass es sicher ist.

Einige Beispielfunktionen:

COLORS = Enum.new(:COLORS, :red => 1, :green => 2, :blue => 3)
=> COLORS(:red => 1, :green => 2, :blue => 3)
COLORS.red == 1 && COLORS.red == :red
=> true

class Car < ActiveRecord::Base    
  attr_enum :color, :COLORS, :red => 1, :black => 2
end
car = Car.new
car.color = :red / "red" / 1 / "1"
car.color
=> Car::COLORS.red
car.color.black?
=> false
Car.red.to_sql
=> "SELECT `cars`.* FROM `cars` WHERE `cars`.`color` = 1"
Car.last.red?
=> true
Oded Niv
quelle
5

Wenn Sie sich Sorgen über Tippfehler mit Symbolen machen, stellen Sie sicher, dass Ihr Code eine Ausnahme auslöst, wenn Sie auf einen Wert mit einem nicht vorhandenen Schlüssel zugreifen. Sie können dies tun, indem Sie fetchanstatt []:

my_value = my_hash.fetch(:key)

oder indem der Hash standardmäßig eine Ausnahme auslöst, wenn Sie einen nicht vorhandenen Schlüssel angeben:

my_hash = Hash.new do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

Wenn der Hash bereits vorhanden ist, können Sie das Verhalten zum Auslösen von Ausnahmen hinzufügen:

my_hash = Hash[[[1,2]]]
my_hash.default_proc = proc do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

Normalerweise müssen Sie sich keine Sorgen um die Tippfehlersicherheit mit Konstanten machen. Wenn Sie einen konstanten Namen falsch schreiben, wird normalerweise eine Ausnahme ausgelöst.

Andrew Grimm
quelle
Anscheinend befürworten Sie das Emulieren von Aufzählungen mit Hashes , ohne dies ausdrücklich zu sagen. Es könnte eine gute Idee sein, Ihre Antwort zu bearbeiten, um dies zu sagen. (Ich habe auch noch einen Bedarf für so etwas wie Aufzählungen in Ruby, und mein erster Ansatz , es zu lösen , ist durch Hashes mit: FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}Dies definiert die Tastensymbole. missing, somethingUsw., und mache auch vergleichbar sie über die zugehörigen Werte.)
Teemu Leisti
Ich meine, ohne es gleich zu Beginn der Antwort zu sagen.
Teemu Leisti
4

Jemand ging voran und schrieb einen Rubinstein namens Renum . Es wird behauptet, das nächstgelegene Java / C # -ähnliche Verhalten zu erhalten. Persönlich lerne ich immer noch Ruby und war ein wenig schockiert, als ich wollte, dass eine bestimmte Klasse eine statische Aufzählung enthält, möglicherweise einen Hash, der über Google nicht so einfach zu finden ist.

Dlamblin
quelle
Ich habe noch nie eine Aufzählung in Ruby gebraucht. Symbole und Konstanten sind idiomatisch und lösen dieselben Probleme, nicht wahr?
Chuck
Wahrscheinlich Chuck; Aber wenn Sie nach einer Aufzählung in Rubin googeln, kommen Sie nicht so weit. Es zeigt Ihnen Ergebnisse für den besten Versuch der Menschen, ein direktes Äquivalent zu finden. Was mich wundert, vielleicht ist es etwas Schönes, das Konzept zusammen zu packen.
Dlamblin
@Chuck Symbole und Konstanten erzwingen nicht, dass ein Wert einer kleinen Menge von Werten sein muss.
David Moles
3

Es hängt alles davon ab, wie Sie Java- oder C # -Aufzählungen verwenden. Wie Sie es verwenden, bestimmt die Lösung, die Sie in Ruby auswählen.

Probieren Sie den nativen SetTyp aus, zum Beispiel:

>> enum = Set['a', 'b', 'c']
=> #<Set: {"a", "b", "c"}>
>> enum.member? "b"
=> true
>> enum.member? "d"
=> false
>> enum.add? "b"
=> nil
>> enum.add? "d"
=> #<Set: {"a", "b", "c", "d"}>
mislav
quelle
9
Warum nicht Symbole verwenden? Set[:a, :b, :c] ?
Dan Rosenstark
2
Viel besser, hier Symbole zu verwenden, IMO.
Collin Graves
3

Kürzlich haben wir eine veröffentlicht Juwel veröffentlicht , das Enums in Ruby implementiert . In meinem Beitrag finden Sie die Antworten auf Ihre Fragen. Außerdem habe ich dort beschrieben, warum unsere Implementierung besser ist als die vorhandenen (tatsächlich gibt es viele Implementierungen dieser Funktion in Ruby noch als Juwelen).

ka8725
quelle
Es ermöglicht selbstinkrementierende Werte, ohne sie explizit anzugeben. +1
dimid
3

Eine andere Lösung ist die Verwendung von OpenStruct. Es ist ziemlich einfach und sauber.

https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html

Beispiel:

# bar.rb
require 'ostruct' # not needed when using Rails

# by patching Array you have a simple way of creating a ENUM-style
class Array
   def to_enum(base=0)
      OpenStruct.new(map.with_index(base).to_h)
   end
end

class Bar

    MY_ENUM = OpenStruct.new(ONE: 1, TWO: 2, THREE: 3)
    MY_ENUM2 = %w[ONE TWO THREE].to_enum

    def use_enum (value)
        case value
        when MY_ENUM.ONE
            puts "Hello, this is ENUM 1"
        when MY_ENUM.TWO
            puts "Hello, this is ENUM 2"
        when MY_ENUM.THREE
            puts "Hello, this is ENUM 3"
        else
            puts "#{value} not found in ENUM"
        end
    end

end

# usage
foo = Bar.new    
foo.use_enum 1
foo.use_enum 2
foo.use_enum 9


# put this code in a file 'bar.rb', start IRB and type: load 'bar.rb'
Roger
quelle
2

Symbole ist der Rubinweg. Manchmal muss man jedoch mit C-Code oder etwas anderem oder Java sprechen, das eine Aufzählung für verschiedene Dinge verfügbar macht.


#server_roles.rb
module EnumLike

  def EnumLike.server_role
    server_Symb=[ :SERVER_CLOUD, :SERVER_DESKTOP, :SERVER_WORKSTATION]
    server_Enum=Hash.new
    i=0
    server_Symb.each{ |e| server_Enum[e]=i; i +=1}
    return server_Symb,server_Enum
  end

end

Dies kann dann so verwendet werden


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Dies kann natürlich abstrakt gemacht werden und Sie können unsere eigene Enum-Klasse würfeln

Jonke
quelle
Großschreiben Sie das zweite Wort in Variablen (z. B. server_Symb) aus einem bestimmten Grund? Sofern es keinen bestimmten Grund gibt, ist es idiomatisch für Variablen snake_case_with_all_lower_caseund für Symbole :lower_case.
Andrew Grimm
1
@ Andrew; Dieses Beispiel stammt aus der Praxis, und in der Netzwerkprotokolldokumentation wurde xxx_Yyy verwendet, sodass der Code in mehreren Sprachen dasselbe Konzept verwendete, sodass Änderungen der Spezifikation verfolgt werden konnten.
Jonke
1
Code Golf : server_Symb.each_with_index { |e,i| server_Enum[e] = i}. Keine Notwendigkeit für i = 0.
Andrew Grimm
2

Ich habe solche Aufzählungen implementiert

module EnumType

  def self.find_by_id id
    if id.instance_of? String
      id = id.to_i
    end 
    values.each do |type|
      if id == type.id
        return type
      end
    end
    nil
  end

  def self.values
    [@ENUM_1, @ENUM_2] 
  end

  class Enum
    attr_reader :id, :label

    def initialize id, label
      @id = id
      @label = label
    end
  end

  @ENUM_1 = Enum.new(1, "first")
  @ENUM_2 = Enum.new(2, "second")

end

dann ist es einfach, Operationen durchzuführen

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
Masuschi
quelle
2

Dies scheint ein bisschen überflüssig zu sein, aber dies ist eine Methode, die ich einige Male verwendet habe, insbesondere wenn ich in XML oder ähnliches integriere.

#model
class Profession
  def self.pro_enum
    {:BAKER => 0, 
     :MANAGER => 1, 
     :FIREMAN => 2, 
     :DEV => 3, 
     :VAL => ["BAKER", "MANAGER", "FIREMAN", "DEV"]
    }
  end
end

Profession.pro_enum[:DEV]      #=>3
Profession.pro_enum[:VAL][1]   #=>MANAGER

Dies gibt mir die Strenge von ac # enum und es ist an das Modell gebunden.

jjk
quelle
Ich würde diesen Ansatz nicht empfehlen, da Sie die Werte manuell einstellen und sicherstellen müssen, dass Sie die richtige Reihenfolge erhalten :VAL. Es wäre besser, mit einem Array zu beginnen und den Hash mit.map.with_index
DaveMongoose
1
Der genaue Punkt ist, sich an einen Wert zu binden, der von Dritten vorgegeben wird. Es geht nicht um Erweiterbarkeit an sich, sondern darum, sich mit externen Einschränkungen befassen zu müssen, die sich auf die Berechenbarkeit innerhalb Ihrer Prozessgrenzen auswirken.
JJJ
Gutes Argument! In diesem Fall ist es definitiv sinnvoll, die Werte anzugeben, aber ich würde gerne die umgekehrte Suche mit .keyoder .invertanstelle eines :VALSchlüssels durchführen ( stackoverflow.com/a/10989394/2208016 )
DaveMongoose
Ja, das ist (wieder bei dir) ein fairer Punkt. Mein Rubin war unelegant und unhandlich. Würde auf keyinvert
jeden Fall
1

Die meisten Leute benutzen Symbole (das ist die :foo_barSyntax). Sie sind eine Art einzigartige undurchsichtige Werte. Symbole gehören zu keinem Aufzählungstyp, daher sind sie keine getreue Darstellung des Aufzählungstyps von C, aber das ist so gut wie es nur geht.

Jan Krüger
quelle
1
irb(main):016:0> num=[1,2,3,4]
irb(main):017:0> alph=['a','b','c','d']
irb(main):018:0> l_enum=alph.to_enum
irb(main):019:0> s_enum=num.to_enum
irb(main):020:0> loop do
irb(main):021:1* puts "#{s_enum.next} - #{l_enum.next}"
irb(main):022:1> end

Ausgabe:

1 - a
2 - b
3 - c
4 - d

Anu
quelle
to_enumgibt Ihnen ein enumera tor , während enumin der C # / Java Sinn ist ein enumera tion
DaveMongoose
1
module Status
  BAD  = 13
  GOOD = 24

  def self.to_str(status)
    for sym in self.constants
      if self.const_get(sym) == status
        return sym.to_s
      end
    end
  end

end


mystatus = Status::GOOD

puts Status::to_str(mystatus)

Ausgabe:

GOOD
Hossein
quelle
1

Manchmal ist alles, was ich brauche, in der Lage zu sein, den Wert von enum abzurufen und seinen Namen ähnlich wie in der Java-Welt zu identifizieren.

module Enum
     def get_value(str)
       const_get(str)
     end
     def get_name(sym)
       sym.to_s.upcase
     end
 end

 class Fruits
   include Enum
   APPLE = "Delicious"
   MANGO = "Sweet"
 end

 Fruits.get_value('APPLE') #'Delicious'
 Fruits.get_value('MANGO') # 'Sweet'

 Fruits.get_name(:apple) # 'APPLE'
 Fruits.get_name(:mango) # 'MANGO'

Dies dient für mich dem Zweck der Aufzählung und hält es auch sehr erweiterbar. Sie können der Enum-Klasse weitere Methoden hinzufügen, und Viola erhält sie in allen definierten Enums kostenlos. zum Beispiel. get_all_names und ähnliches.

dark_src
quelle
0

Ein anderer Ansatz besteht darin, eine Ruby-Klasse mit einem Hash zu verwenden, der Namen und Werte enthält, wie im folgenden RubyFleebie-Blogbeitrag beschrieben . Auf diese Weise können Sie problemlos zwischen Werten und Konstanten konvertieren (insbesondere, wenn Sie eine Klassenmethode hinzufügen, um den Namen für einen bestimmten Wert zu suchen).

Philippe Monnet
quelle
0

Ich denke, der beste Weg, um Aufzählungstypen zu implementieren, sind Symbole, da sich diese so ziemlich wie Ganzzahlen verhalten (wenn es um die Leistung geht, wird object_id verwendet, um Vergleiche anzustellen). Sie müssen sich keine Gedanken über die Indizierung machen und sie sehen in Ihrem Code xD wirklich gut aus

Goreorto
quelle
0

Eine andere Möglichkeit, eine Aufzählung mit konsequenter Gleichstellungsbehandlung nachzuahmen (schamlos von Dave Thomas übernommen). Ermöglicht offene Aufzählungen (ähnlich wie Symbole) und geschlossene (vordefinierte) Aufzählungen.

class Enum
  def self.new(values = nil)
    enum = Class.new do
      unless values
        def self.const_missing(name)
          const_set(name, new(name))
        end
      end

      def initialize(name)
        @enum_name = name
      end

      def to_s
        "#{self.class}::#@enum_name"
      end
    end

    if values
      enum.instance_eval do
        values.each { |e| const_set(e, enum.new(e)) }
      end
    end

    enum
  end
end

Genre = Enum.new %w(Gothic Metal) # creates closed enum
Architecture = Enum.new           # creates open enum

Genre::Gothic == Genre::Gothic        # => true
Genre::Gothic != Architecture::Gothic # => true
Daniel Doubleday
quelle