Làm thế nào để thực hiện Enums trong Ruby?


324

Cách tốt nhất để thực hiện thành ngữ enum trong Ruby là gì? Tôi đang tìm kiếm thứ gì đó mà tôi có thể sử dụng (gần như) như enum Java / C #.


7
@auramo, câu hỏi hay, và sự lựa chọn tuyệt vời cho câu trả lời tốt nhất. Yêu hay ghét nó, bạn không có loại an toàn và (ít nhất là trong Ruby) không có lỗi chính tả. Tôi đã rất vui mừng khi phát hiện ra các enum trong C # và sau đó là Java (chọn một giá trị, nhưng từ những giá trị này!), Ruby không cung cấp một cách thực sự để làm điều đó trong mọi trường hợp.
Dan Rosenstark

2
Vấn đề với câu hỏi này là các enum Java và C # là những thứ khác nhau đáng kể. Một thành viên enum Java là một cá thể đối tượng và một singleton. Một enum Java có thể có một hàm tạo. Ngược lại, enum C # dựa trên các giá trị Nguyên thủy. Những hành vi mà người hỏi đang tìm kiếm? Mặc dù có thể là trường hợp mà trường hợp C # muốn, nhưng Java được đề cập rõ ràng, thay vì C hoặc C ++, vì vậy có một số nghi ngờ. Đối với việc gợi ý rằng không có cách nào là 'an toàn' trong Ruby, điều đó hoàn toàn sai, nhưng bạn phải thực hiện một cái gì đó tinh vi hơn.
dùng1164178

Câu trả lời:


319

Hai lối. Biểu tượng (:foo ký hiệu) hoặc hằng số ( FOOký hiệu).

Các biểu tượng phù hợp khi bạn muốn tăng cường khả năng đọc mà không cần vứt mã bằng các chuỗi ký tự.

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

Hằng số thích hợp khi bạn có một giá trị cơ bản quan trọng. Chỉ cần khai báo một mô-đun để giữ các hằng số của bạn và sau đó khai báo các hằng số trong đó.

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

flags = Foo::BAR | Foo::BAZ # flags = 3

2
Điều gì nếu những enum này quá được lưu trữ vào cơ sở dữ liệu? Ký hiệu biểu tượng sẽ làm việc? Tôi nghi ngờ ...
Phương Nguyễn

Tôi sẽ sử dụng cách tiếp cận hằng nếu tôi đang lưu vào cơ sở dữ liệu. Tất nhiên sau đó bạn phải thực hiện một số loại tra cứu khi rút dữ liệu ra khỏi DB. Bạn cũng có thể sử dụng một cái gì đó như :minnesota.to_skhi lưu vào cơ sở dữ liệu để lưu phiên bản chuỗi của biểu tượng. Rails, tôi tin rằng, có một số phương pháp trợ giúp để đối phó với một số điều này.
mlibby

7
Không phải là một mô-đun sẽ tốt hơn cho các hằng số nhóm - vì bạn sẽ không tạo ra bất kỳ trường hợp nào của nó?
thomthom

3
Chỉ là một nhận xét. Ruby có một chút đau đớn về việc đặt tên cho các quy ước nhưng không thực sự rõ ràng về chúng cho đến khi bạn vượt qua chúng. Tên của enum phải là tất cả các chữ hoa và chữ cái đầu tiên của tên mô-đun phải được viết hoa cho ruby ​​để biết rằng mô-đun là một mô-đun của hằng số.
Rokujolady

3
Không hoàn toàn đúng. Chữ cái đầu tiên của hằng số phải được viết hoa, nhưng không phải tất cả các chữ cái đều phải có. Đây là một vấn đề ưu tiên hội nghị. Ví dụ, tất cả các tên mô-đun và tên lớp thực sự là hằng số.
Michael Brown

59

Tôi ngạc nhiên rằng không ai đã cung cấp một cái gì đó như sau (được thu hoạch từ đá quý RAPI ):

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

Mà có thể được sử dụng như vậy:

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

Thí dụ:

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

Điều này chơi tốt trong các kịch bản cơ sở dữ liệu hoặc khi xử lý các hằng số / enum kiểu C (như trường hợp khi sử dụng FFI , RAPI sử dụng rộng rãi).

Ngoài ra, bạn không phải lo lắng về lỗi chính tả gây ra lỗi thất bại, giống như bạn sử dụng giải pháp loại băm.


1
Đó là một cách tuyệt vời để giải quyết vấn đề cụ thể đó, nhưng lý do không ai đề xuất nó có lẽ phải làm với thực tế là nó không giống như các enum C # / Java.
mlibby

1
Điều này là một chút không đầy đủ, nhưng phục vụ gợi ý tốt như cách bạn có thể thực hiện các giải pháp với một cách tiếp cận năng động. Nó mang một số điểm tương đồng với một C # enum với bộ FlagsAttribution, nhưng giống như các giải pháp dựa trên biểu tượng / hằng số ở trên, đó là một câu trả lời của nhiều người. Vấn đề là câu hỏi ban đầu, được đặt ra trong mục đích của nó (C # và Java không thể thay thế cho nhau). Có nhiều cách để phân loại các đối tượng trong Ruby; chọn đúng một vấn đề phụ thuộc vào vấn đề đang được giải quyết. Các tính năng sao chép lộng lẫy mà bạn không cần là sai. Câu trả lời đúng phải phụ thuộc vào ngữ cảnh.
dùng1164178

52

Cách thành ngữ nhất để làm điều này là sử dụng các biểu tượng. Ví dụ: thay vì:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... bạn chỉ có thể sử dụng các biểu tượng:

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

Đây là một kết thúc mở hơn một chút so với enums, nhưng nó rất phù hợp với tinh thần Ruby.

Biểu tượng cũng thực hiện rất tốt. So sánh hai biểu tượng cho sự bình đẳng, ví dụ, nhanh hơn nhiều so với so sánh hai chuỗi.


107
Vì vậy, tinh thần Ruby là: "Typose sẽ biên dịch"
mxcl

82
Các khung công tác phổ biến của Ruby phụ thuộc rất nhiều vào lập trình siêu dữ liệu thời gian chạy và thực hiện quá nhiều kiểm tra thời gian tải sẽ lấy đi phần lớn sức mạnh biểu cảm của Ruby. Để tránh các vấn đề, hầu hết các lập trình viên Ruby thực hành thiết kế dựa trên thử nghiệm, sẽ tìm thấy không chỉ lỗi chính tả mà còn lỗi logic.
emk

10
@yar: Chà, thiết kế ngôn ngữ là một chuỗi các sự đánh đổi và các tính năng ngôn ngữ tương tác. Nếu bạn muốn có một ngôn ngữ tốt, năng động cao, hãy đi với Ruby, viết bài kiểm tra đơn vị của bạn trước và đi với tinh thần của ngôn ngữ. :-) Nếu đó không phải là thứ bạn đang tìm kiếm, có hàng tá ngôn ngữ tuyệt vời khác, mỗi ngôn ngữ tạo ra sự đánh đổi khác nhau.
emk

10
@emk, tôi đồng ý, nhưng vấn đề cá nhân của tôi là tôi cảm thấy khá thoải mái với Ruby, nhưng tôi không cảm thấy thoải mái khi tái cấu trúc trong Ruby. Và bây giờ tôi đã bắt đầu viết các bài kiểm tra đơn vị (cuối cùng), tôi nhận ra rằng chúng không phải là thuốc chữa bách bệnh: tôi đoán là 1) rằng mã Ruby không được tái cấu trúc ồ ạt, trong thực tế và 2) Ruby không phải là kết thúc -of-the-line về ngôn ngữ động, chính xác bởi vì thật khó để cấu trúc lại tự động. Xem câu hỏi của tôi 2317579 đã được tiếp nhận, thật kỳ lạ, bởi những người ở Smalltalk.
Dan Rosenstark

4
Vâng, nhưng sử dụng các chuỗi đó sẽ không theo tinh thần của ngôn ngữ C #, nó chỉ đơn giản là một thực tiễn tồi.
Ed S.

38

Tôi sử dụng cách tiếp cận sau:

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

Tôi thích nó vì những lợi thế sau:

  1. Nó nhóm các giá trị trực quan như một toàn thể
  2. Nó thực hiện một số kiểm tra thời gian biên dịch (ngược lại với việc chỉ sử dụng các ký hiệu)
  3. Tôi có thể dễ dàng truy cập danh sách tất cả các giá trị có thể: chỉ MY_ENUM
  4. Tôi có thể dễ dàng truy cập các giá trị riêng biệt: MY_VALUE_1
  5. Nó có thể có các giá trị thuộc bất kỳ loại nào, không chỉ là Biểu tượng

Các biểu tượng có thể tốt hơn vì bạn không phải viết tên của lớp bên ngoài, nếu bạn đang sử dụng nó trong một lớp khác ( MyClass::MY_VALUE_1)


4
Tôi nghĩ rằng đây là câu trả lời tốt nhất. Chức năng, cú pháp và chi phí mã tối thiểu đến gần nhất với Java / C #. Ngoài ra, bạn có thể lồng các định nghĩa thậm chí sâu hơn một cấp và vẫn phục hồi tất cả các giá trị với MyClass :: MY_ENUM.flatten. Như một lưu ý phụ, tôi sẽ sử dụng các tên được viết hoa ở đây như là tiêu chuẩn cho các hằng số trong Ruby. MyClass :: MyEnum có thể bị nhầm với tham chiếu đến một lớp con.
Janosch

@Janosch, tôi đã cập nhật tên. cảm ơn vì lời đề nghị
Alexey

Tôi vẫn còn một chút bối rối và liên kết 410'd (không, không phải 404). Bạn có thể cho ví dụ như làm thế nào enum này sẽ được sử dụng?
Shelvacu

17

Nếu bạn đang sử dụng Rails 4.2 trở lên, bạn có thể sử dụng Rails enums.

Rails bây giờ có enums theo mặc định mà không cần bao gồm bất kỳ đá quý.

Điều này rất giống với các tính năng của Java, C ++.

Trích dẫn từ http://edgeapi.rubyonrails.org/groupes/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

7
Như bạn đã nói - không hữu ích nếu OP không sử dụng Rails (hay chính xác hơn là đối tượng không thuộc loại ActiveRecord). Chỉ cần giải thích downvote của tôi là tất cả.
Ger

2
Đây không phải là enum trong Ruby, nó là giao diện ActiveRecord cho Enums trong cơ sở dữ liệu của bạn. Không phải là một giải pháp tổng quát có thể được áp dụng trong bất kỳ trường hợp sử dụng nào khác.
Adam Lassek

Tôi đã đề cập đến điều đó trong câu trả lời của tôi.
vedant

Đây là câu trả lời tốt nhất IFF sử dụng Rails.
theStherSide

Tôi không thích nó vì nó phải được lưu trữ trong cơ sở dữ liệu Rails (để hoạt động) và vì nó cho phép tạo nhiều phiên bản của Conversationlớp - tôi tin rằng nó chỉ cho phép 1 thể hiện.
thiệu

8

Đây là cách tiếp cận của tôi với enums trong Ruby. Tôi đã đi ngắn và ngọt ngào, không nhất thiết phải giống như C nhất. Có suy nghĩ gì không?

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


8

Có lẽ cách tiếp cận nhẹ nhất sẽ là

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

Cách này có giá trị có tên liên quan, như trong Java / C #:

MyConstants::ABC
=> MyConstants::ABC

Để có được tất cả các giá trị, bạn có thể làm

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

Nếu bạn muốn giá trị thứ tự của enum, bạn có thể làm

MyConstants.constants.index :GHI
=> 2

1
IMHO điều này sao chép rất chặt chẽ việc sử dụng và mục đích (loại an toàn) từ Java, ngoài ra, như một vấn đề ưu tiên, các hằng số có thể được định nghĩa như sau:class ABC; end
wik

8

Tôi biết đã lâu lắm rồi anh chàng mới đăng câu hỏi này, nhưng tôi cũng có câu hỏi tương tự và bài đăng này không cho tôi câu trả lời. Tôi muốn một cách dễ dàng để xem con số đại diện cho điều gì, so sánh dễ dàng và hầu hết tất cả hỗ trợ ActiveRecord để tra cứu bằng cách sử dụng cột đại diện cho enum.

Tôi đã không tìm thấy bất cứ điều gì, vì vậy tôi đã thực hiện một triển khai tuyệt vời gọi là yinum cho phép mọi thứ tôi đang tìm kiếm. Được thực hiện rất nhiều thông số kỹ thuật, vì vậy tôi khá chắc chắn rằng nó an toàn.

Một số tính năng ví dụ:

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

5

Nếu bạn lo lắng về lỗi chính tả với các ký hiệu, hãy đảm bảo mã của bạn tăng ngoại lệ khi bạn truy cập một giá trị bằng khóa không tồn tại. Bạn có thể làm điều này bằng cách sử dụng fetchchứ không phải []:

my_value = my_hash.fetch(:key)

hoặc bằng cách làm cho hàm băm tăng một ngoại lệ theo mặc định nếu bạn cung cấp khóa không tồn tại:

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

Nếu hàm băm đã tồn tại, bạn có thể thêm vào hành vi tăng ngoại lệ:

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

Thông thường, bạn không phải lo lắng về sự an toàn của lỗi đánh máy với các hằng số. Nếu bạn viết sai một tên không đổi, nó thường sẽ đưa ra một ngoại lệ.


Có vẻ như bạn đang ủng hộ việc giả lập enum bằng băm , mà không nói rõ ràng như vậy. Nó có thể là một ý tưởng tốt để chỉnh sửa câu trả lời của bạn để nói như vậy. (Tôi cũng đang có nhu cầu về một cái gì đó giống như sự đếm trong Ruby, và cách tiếp cận đầu tiên của tôi để giải quyết nó là bằng cách sử dụng bảng băm: FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}định nghĩa này những biểu tượng chủ chốt. missing, somethingVv, và cũng có thể làm cho họ có thể so sánh qua các giá trị liên quan.)
Teemu Leisti

Ý tôi là, không cần nói như vậy khi bắt đầu câu trả lời.
Teemu Leisti

4

Ai đó đã đi trước và viết một viên đá quý ruby ​​tên là Renum . Nó tuyên bố để có được hành vi giống như Java / C # gần nhất. Cá nhân tôi vẫn đang học Ruby, và tôi đã hơi sốc khi tôi muốn tạo một lớp cụ thể chứa một enum tĩnh, có thể là hàm băm, mà nó không thể tìm thấy chính xác qua google.


Tôi chưa bao giờ cần một enum trong Ruby. Biểu tượng và hằng số là thành ngữ và giải quyết cùng một vấn đề, phải không?
Chuck

Có lẽ là Chuck; nhưng googling cho một enum trong ruby ​​sẽ không đưa bạn đến đó. Nó sẽ cho bạn thấy kết quả cho nỗ lực tốt nhất của mọi người ở mức tương đương trực tiếp. Điều này làm tôi tự hỏi, có lẽ có điều gì đó tốt đẹp về việc khái niệm này được kết hợp với nhau.
dlamblin

@Chuck Biểu tượng và hằng số không thi hành, ví dụ: giá trị phải là một trong những tập hợp giá trị nhỏ.
David Moles

3

Tất cả phụ thuộc vào cách bạn sử dụng enum Java hoặc C #. Cách bạn sử dụng sẽ quyết định giải pháp bạn sẽ chọn trong Ruby.

Hãy thử Setloại bản địa , ví dụ:

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

9
Tại sao không sử dụng ký hiệu Set[:a, :b, :c]?
Dan Rosenstark

2
Thực hành tốt hơn nhiều để sử dụng các biểu tượng ở đây, IMO.
Grain Graves

3

Gần đây, chúng tôi đã phát hành một viên đá quý thực hiện Enums trong Ruby . Trong bài viết của tôi, bạn sẽ tìm thấy câu trả lời cho câu hỏi của bạn. Ngoài ra tôi đã mô tả ở đó lý do tại sao việc triển khai của chúng tôi tốt hơn so với hiện tại (thực tế có nhiều triển khai tính năng này trong Ruby nhưng là đá quý).


Nó cho phép tự tăng giá trị, mà không nêu rõ chúng. +1
mờ

3

Một giải pháp khác là sử dụng OpenStabase. Nó khá thẳng về phía trước và sạch sẽ.

https://ruby-doc.org/stdlib-2.3.1/libdoc/osturation/rdoc/OpenSturation.html

Thí dụ:

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

2

Biểu tượng là cách ruby. Tuy nhiên, đôi khi người ta cần nói chuyện với một số mã C hoặc một cái gì đó hoặc Java để lộ một số enum cho những thứ khác nhau.


#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

Điều này sau đó có thể được sử dụng như thế này


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Điều này tất nhiên có thể được thực hiện trừu tượng và bạn có thể cuộn lớp Enum của riêng chúng tôi


Bạn có viết hoa từ thứ hai trong các biến (ví dụ server_Symb) cho một lý do cụ thể không? Trừ khi có một lý do cụ thể, nó là thành ngữ cho các biến snake_case_with_all_lower_casevà cho các ký hiệu là :lower_case.
Andrew Grimm

1
@Andrew; ví dụ này được lấy từ một điều trong thế giới thực và tài liệu giao thức mạng đã sử dụng xxx_Yyy, vì vậy mã trong một số ngôn ngữ đã sử dụng cùng một khái niệm để người ta có thể theo dõi các thay đổi của đặc tả.
Jonke

1
Mã golf: server_Symb.each_with_index { |e,i| server_Enum[e] = i} . Không cần i = 0.
Andrew Grimm

2

Tôi đã thực hiện enum như thế

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

sau đó thật dễ dàng để thực hiện các hoạt động

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values

2

Điều này có vẻ hơi thừa, nhưng đây là một phương pháp mà tôi đã sử dụng một vài lần, đặc biệt là khi tôi đang tích hợp với xml hoặc một số như vậy.

#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

Điều này mang lại cho tôi sự nghiêm ngặt của ac # enum và nó được gắn với mô hình.


Tôi sẽ không tư vấn cho phương pháp này vì nó phụ thuộc vào bạn tự đặt các giá trị và đảm bảo bạn nhận được đơn hàng ngay :VAL. Sẽ tốt hơn khi bắt đầu với một mảng và xây dựng hàm băm bằng cách sử dụng.map.with_index
DaveMongoose

1
Điểm chính xác là buộc bạn vào một giá trị được quyết định bởi các bên thứ ba. Đó không phải là về khả năng mở rộng mỗi se, mà là phải xử lý các ràng buộc bên ngoài ảnh hưởng đến khả năng tính toán trong ranh giới quy trình của bạn.
jjk

Điểm công bằng! Trong trường hợp đó, việc xác định các giá trị là điều hợp lý, nhưng tôi có xu hướng tìm kiếm ngược lại bằng .keyhoặc .invertthay vì một :VALkhóa ( stackoverflow.com/a/10989394/2208016 )
DaveMongoose

Vâng, đó là (trở lại với bạn) một điểm công bằng. Viên ruby ​​của tôi không phù hợp và khó sử dụng. Sẽ sử dụng keyhoặcinvert
jjk

1

Hầu hết mọi người sử dụng các ký hiệu (đó là :foo_barcú pháp). Chúng là những giá trị mờ đục độc đáo. Các biểu tượng không thuộc bất kỳ loại enum nào vì vậy chúng không thực sự là đại diện trung thành của loại enum của C nhưng điều này khá tốt như nó có.


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

Đầu ra:

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


to_enumcung cấp cho bạn một tor enumera , trong khi enumtheo nghĩa C # / Java là một 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)

Đầu ra:

GOOD

1

Đôi khi tất cả những gì tôi cần là có thể lấy giá trị của enum và xác định tên của nó tương tự như thế giới 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'

Điều này với tôi phục vụ mục đích của enum và giữ cho nó rất mở rộng. Bạn có thể thêm nhiều phương thức vào lớp Enum và viola có được chúng miễn phí trong tất cả các enum đã xác định. ví dụ. get_all_names và những thứ như thế.


0

Một cách tiếp cận khác là sử dụng một lớp Ruby với hàm băm chứa tên và giá trị như được mô tả trong bài đăng trên blog của RubyFleebie sau đây . Điều này cho phép bạn dễ dàng chuyển đổi giữa các giá trị và hằng số (đặc biệt nếu bạn thêm một phương thức lớp để tra cứu tên cho một giá trị nhất định).


0

Tôi nghĩ rằng cách tốt nhất để thực hiện phép liệt kê như các kiểu là với các ký hiệu vì phần lớn hoạt động như số nguyên (khi nói đến biểu thức, object_id được sử dụng để so sánh); bạn không cần phải lo lắng về việc lập chỉ mục và chúng trông thực sự gọn gàng trong mã của bạn xD


0

Một cách khác để bắt chước một enum với cách xử lý bình đẳng nhất quán (được chấp nhận một cách không biết xấu hổ từ Dave Thomas). Cho phép các enum mở (giống như các ký hiệu) và các enum đóng (được xác định trước).

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.