348

Rubyでenumイディオムを実装する最良の方法は何ですか? Java/C# 列挙型のように (ほぼ) 使用できるものを探しています。

4

25 に答える 25

347

ふたつのやり方。記号(:foo表記)または定数(FOO表記)。

シンボルは、コードにリテラル文字列を散らかさずに読みやすさを向上させたい場合に適しています。

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

定数は、重要な基礎となる値がある場合に適しています。定数を保持するモジュールを宣言し、その中で定数を宣言するだけです。

module Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end
 
flags = Foo::BAR | Foo::BAZ # flags = 3

2021-01-17 追加

列挙型の値を渡し (データベースに保存するなど)、その値をシンボルに変換できるようにする必要がある場合は、両方のアプローチのマッシュアップがあります。

COMMODITY_TYPE = {
  currency: 1,
  investment: 2,
}

def commodity_type_string(value)
  COMMODITY_TYPE.key(value)
end

COMMODITY_TYPE[:currency]

andrew-grimm の回答に触発されたこのアプローチhttps://stackoverflow.com/a/5332950/13468

また、これを解決するには多くの方法があり、気になる他の言語の列挙型が何であるかに要約されるため、ここで残りの回答を読むことをお勧めします

于 2008-09-16T19:32:30.110 に答える
60

次のようなものを誰も提供していないことに驚いています ( RAPI gem から収集):

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

次のように使用できます。

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

例:

>> 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

これは、データベースのシナリオや、C スタイルの定数/列挙型を扱う場合 ( RAPI が広範囲に使用するFFIを使用する場合など) に適しています。

また、ハッシュタイプのソリューションを使用する場合のように、タイプミスが原因でサイレント エラーが発生することを心配する必要もありません。

于 2011-05-29T21:19:58.597 に答える
52

これを行う最も慣用的な方法は、シンボルを使用することです。たとえば、次の代わりに:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

...記号を使用できます:

# 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)

これは enum よりも少し制限がありませんが、Ruby の精神によく合います。

シンボルも非常にうまく機能します。たとえば、2 つのシンボルが等しいかどうかを比較することは、2 つの文字列を比較するよりもはるかに高速です。

于 2008-09-16T19:06:04.790 に答える
47

私は次のアプローチを使用します。

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

次の利点があるので気に入っています。

  1. 値を 1 つの全体として視覚的にグループ化する
  2. コンパイル時にいくつかのチェックを行います(シンボルを使用するだけとは対照的です)
  3. すべての可能な値のリストに簡単にアクセスできます。MY_ENUM
  4. 個別の値に簡単にアクセスできます。MY_VALUE_1
  5. シンボルだけでなく、任意の型の値を持つことができます

別のクラスで使用している場合は、外部クラスの名前を記述する必要がないため、シンボルの方がよい場合があります ( MyClass::MY_VALUE_1)

于 2011-04-15T10:46:46.657 に答える
11

男がこの質問を投稿してから長い時間が経っていることは知っていますが、同じ質問があり、この投稿では答えが得られませんでした. 数値が何を表しているかを確認する簡単な方法、簡単な比較、そして何よりも、列挙型を表す列を使用したルックアップに対する ActiveRecord のサポートが必要でした。

何も見つからなかったので、探していたものすべてを許可するyinumという素晴らしい実装を作成しました。スペックもしっかりしているので安心です。

機能の例:

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
于 2012-12-07T13:46:41.670 に答える
10

ruby-enum gem https://github.com/dblock/ruby-enumを確認してください。

class Gender
  include Enum

  Gender.define :MALE, "male"
  Gender.define :FEMALE, "female"
end

Gender.all
Gender::MALE
于 2011-03-16T21:46:48.707 に答える
10

これは、Ruby の列挙型に対する私のアプローチです。私は短くて甘いものを求めていましたが、必ずしも最もCのようなものではありませんでした. 何かご意見は?

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
=> 5
于 2012-07-12T15:48:35.760 に答える
8

おそらく、最良の軽量アプローチは

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

このように、値には Java/C# のように名前が関連付けられています。

MyConstants::ABC
=> MyConstants::ABC

すべての値を取得するには、次のことができます

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

列挙型の序数値が必要な場合は、次のことができます

MyConstants.constants.index :GHI
=> 2
于 2014-12-19T21:50:52.597 に答える
7

シンボルのタイプミスが心配な場合は、存在しないキーで値にアクセスしたときにコードで例外が発生することを確認してください。fetchではなく、[]次を使用してこれを行うことができます。

my_value = my_hash.fetch(:key)

または、存在しないキーを指定した場合、デフォルトでハッシュが例外を発生させるようにします。

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

ハッシュが既に存在する場合は、例外を発生させる動作を追加できます。

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

通常、定数のタイプミスの安全性について心配する必要はありません。定数名のスペルを間違えると、通常は例外が発生します。

于 2011-03-16T23:14:48.757 に答える
4

それはすべて、JavaまたはC#列挙型の使用方法によって異なります。どのように使用するかによって、Rubyで選択するソリューションが決まります。

Setたとえば、ネイティブタイプを試してください。

>> 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"}>
于 2008-09-16T20:35:24.310 に答える
4

誰かがRenumという ruby​​ gem を書きました。最も近い Java/C# のような動作が得られると主張しています。個人的にはまだ Ruby を学んでいますが、特定のクラスに静的な列挙型 (おそらくハッシュ) を含めたいと思ったとき、Google で簡単に見つけられないことに少しショックを受けました。

于 2009-03-04T20:47:49.597 に答える
2

これは少し不必要に思えますが、これは私が何度か使用した方法論であり、特に xml などと統合している場合に当てはまります。

#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

これにより、ac# enum の厳密さが得られ、モデルに結び付けられます。

于 2013-04-16T20:09:44.973 に答える
2

シンボルはルビーのやり方です。ただし、さまざまな列挙型を公開する C コードや何か、または Java と対話する必要がある場合があります。


#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

これは、次のように使用できます


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

これはもちろん抽象化することができ、独自の Enum クラスをロールすることができます

于 2008-10-02T20:47:41.460 に答える
2

そのような列挙型を実装しました

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

その後、操作は簡単です

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
于 2012-02-28T13:38:05.410 に答える
1

ほとんどの人は記号を使用します (それが:foo_bar構文です)。それらは一種の一意の不透明な値です。シンボルはどの列挙型にも属さないので、実際には C の列挙型を忠実に表現したものではありませんが、これで十分です。

于 2008-09-16T19:04:45.887 に答える
1

必要なのは、enum の値を取得して、Java の世界に似た名前を識別できることだけです。

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'

これは私にとって列挙型の目的を果たし、非常に拡張可能に保ちます。Enum クラスにさらにメソッドを追加すると、viola は定義されたすべての列挙型でそれらを無料で取得できます。例えば。get_all_names など。

于 2015-08-21T21:54:19.370 に答える
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

出力:

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

于 2012-03-06T11:38:10.390 に答える
0

型のような列挙型を実装する最良の方法は、シンボルを使用することだと思います。インデックス作成について心配する必要はなく、コード内で非常にきれいに見えます xD

于 2010-04-03T23:57:49.383 に答える
0

もう 1 つの方法は、次のRubyFleebie ブログ投稿で説明されているように、名前と値を含むハッシュを持つ Ruby クラスを使用することです。これにより、値と定数の間で簡単に変換できます (特に、特定の値の名前を検索するクラス メソッドを追加する場合)。

于 2009-09-29T18:07:32.430 に答える
0

一貫した等価処理で列挙型を模倣する別の方法 (恥知らずに Dave Thomas から採用)。開いた列挙型 (シンボルによく似ています) と閉じた (定義済みの) 列挙型を許可します。

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
于 2012-12-30T01:44:43.050 に答える