문제

Ruby에서 enum 관용구를 구현하는 가장 좋은 방법은 무엇입니까?Java/C# 열거형과 같이 (거의) 사용할 수 있는 것을 찾고 있습니다.

도움이 되었습니까?

해결책

두 가지 방법.기호(: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

다른 팁

나는 아무도 다음과 같은 것을 제안하지 않았다는 사실에 놀랐습니다. 라피 보석):

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 스타일 상수/열거형을 처리할 때(사용하는 경우와 마찬가지로) 잘 작동합니다. FFI, RAPI가 광범위하게 사용함).

또한 해시 유형 솔루션을 사용할 때처럼 오타로 인해 자동 오류가 발생하는 것에 대해 걱정할 필요가 없습니다.

이를 수행하는 가장 관용적인 방법은 기호를 사용하는 것입니다.예를 들면 다음과 같습니다.

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)

이는 열거형보다 좀 더 개방적이지만 Ruby 정신에 잘 맞습니다.

기호도 매우 잘 수행됩니다.예를 들어 두 기호의 동등성을 비교하는 것은 두 문자열을 비교하는 것보다 훨씬 빠릅니다.

나는 다음 접근 방식을 사용합니다.

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

나는 다음과 같은 장점 때문에 그것을 좋아합니다:

  1. 가치를 시각적으로 하나의 전체로 그룹화합니다.
  2. 일부 컴파일 시간 검사를 수행합니다(기호만 사용하는 것과는 대조적).
  3. 가능한 모든 값 목록에 쉽게 액세스할 수 있습니다.단지 MY_ENUM
  4. 고유한 값에 쉽게 액세스할 수 있습니다. MY_VALUE_1
  5. Symbol뿐만 아니라 모든 유형의 값을 가질 수 있습니다.

다른 클래스에서 사용하는 경우 외부 클래스의 이름을 쓸 필요가 없기 때문에 기호가 더 나을 수 있습니다(MyClass::MY_VALUE_1)

Rails 4.2 이상을 사용하는 경우 Rails 열거형을 사용할 수 있습니다.

Rails에는 이제 보석을 포함할 필요 없이 기본적으로 열거형이 있습니다.

이는 Java, C++ 열거형과 매우 유사하며 기능도 더 많습니다.

에서 인용 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

이것이 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
=> 3

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

그 사람이 이 질문을 게시한 지 오랜 시간이 흘렀다는 것을 알고 있지만, 저도 같은 질문을 갖고 있었는데 이 게시물에서는 답변을 얻을 수 없었습니다.나는 숫자가 무엇을 나타내는지 쉽게 확인할 수 있는 방법, 쉬운 비교, 그리고 무엇보다도 열거형을 나타내는 열을 사용한 조회에 대한 ActiveRecord 지원을 원했습니다.

아무것도 찾지 못했기 때문에 다음과 같은 멋진 구현을 만들었습니다. 이눔 내가 찾고 있던 모든 것을 허용했습니다.엄청난 사양을 만들어서 안전하다고 확신합니다.

몇 가지 예시 기능은 다음과 같습니다.

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

기호 오타가 걱정된다면 존재하지 않는 키를 사용하여 값에 액세스할 때 코드에서 예외가 발생하는지 확인하세요.다음을 사용하여 이 작업을 수행할 수 있습니다. 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

일반적으로 상수의 오타 안전에 대해 걱정할 필요가 없습니다.상수 이름의 철자를 잘못 입력하면 일반적으로 예외가 발생합니다.

아마도 가장 가벼운 접근 방식은 다음과 같습니다.

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

누군가가 앞서서 다음과 같은 루비 보석을 작성했습니다. 레넘.이는 동작과 유사한 Java/C#을 얻는다고 주장합니다.개인적으로 저는 아직 Ruby를 배우고 있는데 특정 클래스에 정적 열거형(해시일 수도 있음)을 포함하려고 했을 때 Google을 통해 쉽게 찾을 수 없다는 사실에 약간 충격을 받았습니다.

그것은 모두 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"}>

최근에 우리는 보석 구현하는 Ruby의 열거형.내 우편 귀하의 질문에 대한 답변을 찾을 수 있습니다.또한 나는 우리의 구현이 기존 구현보다 나은 이유를 설명했습니다(실제로 Ruby에는 아직 보석으로 이 기능을 구현한 것이 많이 있습니다).

기호는 루비 방식입니다.그러나 때로는 다양한 항목에 대한 일부 열거형을 노출하는 일부 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 클래스를 롤링할 수도 있습니다.

나는 그런 열거 형을 구현했습니다

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

이것은 약간 불필요한 것처럼 보이지만 이것은 특히 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

이는 C# 열거형의 엄격함을 제공하며 모델에 연결됩니다.

또 다른 솔루션은 OpenStruct를 사용하는 것입니다.꽤 간단하고 깨끗합니다.

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

예:

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

대부분의 사람들은 기호를 사용합니다. :foo_bar 통사론).그것들은 일종의 고유하고 불투명한 값입니다.기호는 열거형 스타일 유형에 속하지 않으므로 실제로 C의 열거형 유형을 충실하게 표현하지는 않지만 이 정도는 가능한 한 좋습니다.

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 - 에이
2-b
3-c
4-d

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)

산출:

GOOD

때때로 나에게 필요한 것은 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의 목적을 제공하고 확장성을 유지합니다.Enum 클래스에 더 많은 메서드를 추가하면 정의된 모든 열거형에서 해당 메서드를 무료로 얻을 수 있습니다.예를 들어.get_all_names 등.

또 다른 접근 방식은 다음에 설명된 대로 이름과 값이 포함된 해시와 함께 Ruby 클래스를 사용하는 것입니다. RubyFleebie 블로그 게시물.이를 통해 값과 상수 사이를 쉽게 변환할 수 있습니다(특히 주어진 값의 이름을 조회하기 위해 클래스 메서드를 추가하는 경우).

유형과 같은 열거형을 구현하는 가장 좋은 방법은 기호를 사용하는 것입니다. 왜냐하면 거의 정수처럼 동작하기 때문입니다(성능에 관해서는 object_id를 사용하여 비교합니다).인덱싱에 대해 걱정할 필요가 없으며 코드에서 정말 깔끔하게 보입니다 xD

일관된 동등 처리로 열거형을 모방하는 또 다른 방법입니다(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

이넘을 사용해 보세요.https://github.com/alfa-jpn/inum

class Color < Inum::Base
  define :RED
  define :GREEN
  define :BLUE
end
Color::RED 
Color.parse('blue') # => Color::BLUE
Color.parse(2)      # => Color::GREEN

더보기 https://github.com/alfa-jpn/inum#usage

빠르고 지저분하며 C#과 같은 느낌입니다.

class FeelsLikeAnEnum
  def self.Option_1() :option_1 end
  def self.Option_2() :option_2 end
  def self.Option_3() :option_3 end
end

Enum을 사용하는 것처럼 사용하십시오.

method_that_needs_options(FeelsLikeAnEnum.Option_1)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top