วิธีการใช้ Enums ใน Ruby


323

อะไรคือวิธีที่ดีที่สุดในการใช้สำนวน enum ใน Ruby ฉันกำลังมองหาสิ่งที่ฉันสามารถใช้ (เกือบ) เช่น Java / C # enums


7
@auramo คำถามที่ดีและทางเลือกที่ดีสำหรับคำตอบที่ดีที่สุด รักหรือเกลียดมันคุณจะไม่ได้รับความปลอดภัยจากการพิมพ์และ (อย่างน้อยก็ใน Ruby) ไม่มีความปลอดภัยในการพิมพ์ผิด ฉันตื่นเต้นเมื่อฉันค้นพบ enums ใน C # และต่อมาใน Java (เลือกค่า แต่จากสิ่งเหล่านี้!) Ruby ไม่ได้ให้วิธีการทำเช่นนั้นในทุกกรณี
Dan Rosenstark

2
ปัญหาของคำถามนี้คือ Java และ C # enums นั้นแตกต่างกันอย่างมาก สมาชิก Java enum เป็นอินสแตนซ์ของวัตถุและซิงเกิล Java enum สามารถมี Constructor ได้ ในทางตรงกันข้าม C # enums ยึดตามค่าดั้งเดิม ผู้ถามกำลังมองหาพฤติกรรมใด แม้ว่าจะเป็นกรณีที่ต้องการตัวพิมพ์ใหญ่ # แต่ Java ถูกกล่าวถึงอย่างชัดเจนมากกว่า C หรือ C ++ ดังนั้นจึงมีข้อสงสัย สำหรับการแนะนำว่าไม่มีทางที่จะ 'ปลอดภัย' ในทับทิมนั่นเป็นเท็จอย่างโปร่งใส แต่คุณต้องใช้สิ่งที่ซับซ้อนกว่านี้
1164178

คำตอบ:


318

สองทาง. สัญลักษณ์ ( :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

2
เกิดอะไรขึ้นถ้า enum เหล่านี้ถูกเก็บไว้ในฐานข้อมูลมากเกินไป? สัญกรณ์สัญลักษณ์จะทำงานอย่างไร ฉันสงสัย ...
PhươngNguyễn

ฉันจะใช้วิธีคงที่หากฉันบันทึกลงในฐานข้อมูล แน่นอนว่าคุณต้องทำการค้นหาบางอย่างเมื่อดึงข้อมูลออกจากฐานข้อมูล คุณสามารถใช้บางอย่างเช่น:minnesota.to_sเมื่อบันทึกลงในฐานข้อมูลเพื่อบันทึกสัญลักษณ์สตริงรุ่น ผมเชื่อว่า Rails มีวิธีการช่วยเหลือในการจัดการกับสิ่งนี้
mlibby

7
โมดูลจะไม่ดีไปกว่าการจัดกลุ่มค่าคงที่หรือไม่เพราะคุณจะไม่ทำกรณีใด ๆ
Thomthom

3
เพียงแสดงความคิดเห็น รูบี้เจ็บปวดเล็กน้อยเกี่ยวกับการตั้งชื่อการประชุม แต่ไม่ค่อยชัดเจนเกี่ยวกับพวกเขาจนกว่าคุณจะเดินทางข้ามพวกเขา ชื่อของ enums จะต้องเป็นตัวพิมพ์ใหญ่ทั้งหมดและตัวอักษรตัวแรกของชื่อโมดูลจะต้องเป็นตัวพิมพ์ใหญ่สำหรับทับทิมที่จะรู้ว่าโมดูลเป็นโมดูลของค่าคงที่
Rokujolady

3
ไม่จริงทั้งหมด อักษรตัวแรกของค่าคงที่ต้องเป็นตัวพิมพ์ใหญ่ แต่ไม่จำเป็นต้องเป็นตัวอักษรทั้งหมด นี่เป็นเรื่องของการตั้งค่าการประชุม ตัวอย่างเช่นชื่อโมดูลและชื่อคลาสทั้งหมดจะเป็นค่าคงที่เช่นกัน
Michael Brown

59

ฉันประหลาดใจที่ไม่มีใครเสนอสิ่งต่อไปนี้ (เก็บเกี่ยวจาก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 / enums (เช่นในกรณีที่ใช้FFIซึ่ง RAPI ใช้อย่างกว้างขวาง)

นอกจากนี้คุณไม่ต้องกังวลกับความผิดพลาดที่ก่อให้เกิดความล้มเหลวเงียบเช่นเดียวกับที่คุณใช้โซลูชั่นแฮชประเภท


1
นั่นเป็นวิธีที่ดีในการแก้ปัญหานั้น แต่เหตุผลที่ไม่มีใครแนะนำว่ามันอาจเกี่ยวข้องกับความจริงที่ว่ามันไม่เหมือนกับ C # / Java enums
mlibby

1
นี่เป็นบิตที่ไม่สมบูรณ์ แต่ทำหน้าที่เป็นคำแนะนำที่ดีเกี่ยวกับวิธีที่คุณอาจใช้โซลูชันด้วยแนวทางแบบไดนามิก มันมีความคล้ายคลึงกับ C # enum ด้วยชุด FlagsAttribute แต่เช่นเดียวกับสัญลักษณ์ / การแก้ปัญหาพื้นฐานที่คงที่ด้านบนมันเป็นคำตอบเดียวของหลาย ๆ ปัญหาคือคำถามเดิมซึ่งสับสนในเจตนา (C # และ Java ไม่สามารถใช้แทนกันได้) มีหลายวิธีในการลงรายละเอียดวัตถุใน Ruby; การเลือกสิ่งที่ถูกต้องขึ้นอยู่กับปัญหาที่กำลังแก้ไข ฟีเจอร์การจำลองแบบที่คุณไม่ต้องการนั้นถูกเข้าใจผิด คำตอบที่ถูกต้องขึ้นอยู่กับบริบท
1164178

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)

นี่เป็นปลายเปิดที่มากกว่า enums เล็กน้อย แต่มันเข้ากันได้ดีกับวิญญาณทับทิม

สัญลักษณ์ยังทำงานได้ดีมาก ยกตัวอย่างเช่นการเปรียบเทียบสองสัญลักษณ์เพื่อความเท่าเทียมกันนั้นเร็วกว่าการเปรียบเทียบสองสาย


107
ดังนั้นจิตวิญญาณของทับทิมคือ "Typos จะรวบรวม"
mxcl

82
เฟรมเวิร์ก Ruby ที่ได้รับความนิยมนั้นอาศัยการทำงานของโปรแกรม metaprogramming และการตรวจสอบความเร็วในการโหลดมากเกินไปจะทำให้พลังการแสดงผลของ Ruby ส่วนใหญ่หมดไป เพื่อหลีกเลี่ยงปัญหาโปรแกรมเมอร์ Ruby ส่วนใหญ่ฝึกฝนการออกแบบโดยใช้การทดสอบซึ่งไม่เพียง แต่จะพิมพ์ผิด แต่ยังมีข้อผิดพลาดทางตรรกะ
emk

10
@yar: การออกแบบภาษาเป็นชุดของการแลกเปลี่ยนและคุณลักษณะภาษามีการโต้ตอบ หากคุณต้องการภาษาที่ดีและมีพลวัตสูงไปกับ Ruby เขียนหน่วยการทดสอบของคุณก่อนและไปด้วยจิตวิญญาณของภาษา :-) หากนั่นไม่ใช่สิ่งที่คุณกำลังมองหามีภาษาที่ยอดเยี่ยมอื่น ๆ อีกมากมายที่นั่นแต่ละภาษาทำให้การแลกเปลี่ยนที่แตกต่างกัน
emk

10
@emk ฉันเห็นด้วย แต่ปัญหาส่วนตัวของฉันคือฉันรู้สึกสะดวกสบายใน Ruby แต่ฉันไม่รู้สึกสบายใจที่จะทำการ refactoring ใน Ruby และตอนนี้ฉันเริ่มเขียนการทดสอบหน่วย (ในที่สุด) ฉันรู้ว่าพวกเขาไม่ใช่ยาครอบจักรวาล: ฉันเดาว่า 1) รหัสทับทิมไม่ได้รับการฟื้นฟูอย่างหนาแน่นบ่อยครั้งในทางปฏิบัติและ 2) ทับทิมไม่ใช่จุดจบ -of-the-line ในแง่ของภาษาแบบไดนามิกได้อย่างแม่นยำเพราะมันยากที่จะ refactor โดยอัตโนมัติ ดูคำถามของฉัน 2317579 ที่ถูกครอบงำโดยแปลก ๆ โดยกลุ่มคนสมอลล์ทอล์ค
Dan Rosenstark

4
ใช่ แต่การใช้สตริงเหล่านั้นไม่ได้อยู่ในจิตวิญญาณของภาษา C # มันเป็นเพียงการปฏิบัติที่ไม่ดี
Ed S.

38

ฉันใช้วิธีการต่อไปนี้:

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

ฉันชอบมันสำหรับข้อดีดังต่อไปนี้:

  1. มันจัดกลุ่มค่าสายตาเป็นหนึ่งเดียว
  2. มันทำการตรวจสอบเวลาการคอมไพล์ (ตรงกันข้ามกับใช้สัญลักษณ์)
  3. ฉันสามารถเข้าถึงรายการค่าที่เป็นไปได้ทั้งหมด: เพียงแค่ MY_ENUM
  4. ฉันสามารถเข้าถึงค่าที่แตกต่างได้อย่างง่ายดาย: MY_VALUE_1
  5. สามารถมีค่าได้ทุกประเภทไม่ใช่แค่สัญลักษณ์

สัญลักษณ์อาจดีกว่าเพราะคุณไม่ต้องเขียนชื่อคลาสภายนอกถ้าคุณใช้มันในคลาสอื่น ( MyClass::MY_VALUE_1)


4
ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุด ฟังก์ชั่น, ไวยากรณ์และรหัสค่าใช้จ่ายน้อยที่สุดมาใกล้กับ Java / C # นอกจากนี้คุณสามารถซ้อนคำจำกัดความได้ลึกกว่าระดับหนึ่งและยังสามารถกู้คืนค่าทั้งหมดด้วย MyClass :: MY_ENUM.flatten ในฐานะด้านข้างฉันจะใช้ชื่อตัวพิมพ์ใหญ่ที่นี่เช่นเดียวกับมาตรฐานสำหรับค่าคงที่ใน Ruby MyClass :: MyEnum อาจเข้าใจผิดสำหรับการอ้างอิงถึงคลาสย่อย
Janosch

@ Janosch ฉันได้อัปเดตชื่อแล้ว ขอบคุณสำหรับคำแนะนำ
Alexey

ฉันยังสับสนอยู่เล็กน้อยและลิงค์ 410 (ไม่ไม่ใช่ 404) คุณสามารถยกตัวอย่างว่าจะใช้ Enum นี้ได้อย่างไร
Shelvacu

17

หากคุณใช้ Rails 4.2 หรือสูงกว่าคุณสามารถใช้ Rails enums ได้

Rails จะมีค่าเริ่มต้นโดยไม่จำเป็นต้องใส่อัญมณีใด ๆ

นี้คล้ายกันมาก (และอื่น ๆ พร้อมคุณสมบัติ) กับ Java, C ++ enums

อ้างถึงจาก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

7
อย่างที่คุณพูด - ไม่มีประโยชน์ถ้า OP ไม่ได้ใช้ Rails (หรือแม่นยำกว่าวัตถุนั้นไม่ใช่ประเภท ActiveRecord) เพียงอธิบาย downvote ของฉันคือทั้งหมด
Ger

2
สิ่งเหล่านี้ไม่ใช่ enums ใน Ruby มันเป็นอินเตอร์เฟส ActiveRecord เพื่อ Enums ในฐานข้อมูลของคุณ ไม่ใช่โซลูชัน generalizable ที่สามารถนำไปใช้ในกรณีการใช้งานอื่น ๆ
Adam Lassek

ฉันได้พูดถึงเรื่องนี้ในคำตอบของฉันแล้ว
vedant

นี่เป็นคำตอบที่ดีที่สุดในการใช้ Rails
theUtherSide

ฉันไม่ชอบเพราะต้องถูกเก็บไว้ในฐานข้อมูล Rails (เพื่อใช้งาน) และเพราะช่วยให้สามารถสร้างConversationคลาสได้หลายอินสแตนซ์- ฉันเชื่อว่าต้องอนุญาตเพียง 1 อินสแตนซ์เท่านั้น
prograils

8

นี่เป็นแนวทางของฉันในการสมัครสมาชิกรูบี ฉันกำลังจะสั้นและหวานไม่จำเป็นต้องเป็นแบบ 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

8

ตรวจสอบอัญมณีทับทิม enum, https://github.com/dblock/ruby-enum

class Gender
  include Enum

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

Gender.all
Gender::MALE

8

บางทีวิธีการที่มีน้ำหนักเบาที่สุดอาจเป็นไปได้

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

วิธีนี้ค่ามีชื่อที่เกี่ยวข้องเช่นใน Java / C #:

MyConstants::ABC
=> MyConstants::ABC

ในการรับค่าทั้งหมดคุณสามารถทำได้

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

หากคุณต้องการค่าลำดับของ enum คุณสามารถทำได้

MyConstants.constants.index :GHI
=> 2

1
IMHO นี้เลียนแบบการใช้งานและวัตถุประสงค์อย่างใกล้ชิด (ประเภทความปลอดภัย) จาก Java เช่นเดียวกับเรื่องของความชอบค่าคงที่สามารถกำหนดได้ดังนี้:class ABC; end
wik

8

ฉันรู้ว่าเป็นเวลานานแล้วที่คนโพสต์คำถามนี้ แต่ฉันมีคำถามเดียวกันและโพสต์นี้ไม่ได้ให้คำตอบกับฉัน ฉันต้องการวิธีง่ายๆในการดูว่าตัวเลขแสดงถึงการเปรียบเทียบง่ายและส่วนใหญ่ของการสนับสนุน ActiveRecord ทั้งหมดสำหรับการค้นหาโดยใช้คอลัมน์ที่เป็นตัวแทนของ enum

ฉันไม่พบอะไรเลยดังนั้นฉันจึงใช้งานได้อย่างยอดเยี่ยมที่เรียกว่า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

5

หากคุณกังวลเกี่ยวกับความผิดพลาดที่มีสัญลักษณ์ตรวจสอบให้แน่ใจว่าโค้ดของคุณยกข้อยกเว้นเมื่อคุณเข้าถึงค่าด้วยรหัสที่ไม่มีอยู่จริง คุณสามารถทำได้โดยใช้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

โดยปกติคุณไม่ต้องกังวลเกี่ยวกับความปลอดภัยของการพิมพ์ผิดกับค่าคงที่ หากคุณสะกดชื่อคงที่มันมักจะทำให้เกิดข้อยกเว้น


ดูเหมือนว่าคุณกำลังสนับสนุนการเลียนแบบ enums ด้วยแฮชโดยไม่บอกอย่างชัดเจน มันอาจเป็นความคิดที่ดีที่จะแก้ไขคำตอบของคุณเพื่อพูดอย่างนั้น (ผมในปัจจุบันยังมีความต้องการบางอย่างเช่น enums ในรูบีและวิธีแรกของฉันที่จะแก้มันคือการใช้แฮช: FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}กำหนดนี้สัญลักษณ์ที่สำคัญ. missing, somethingฯลฯ และยังทำให้พวกเขาเปรียบได้ผ่านทางค่าที่เกี่ยวข้อง.)
เทียมู Leisti

ฉันหมายถึงโดยไม่พูดอย่างนั้นในตอนเริ่มต้นของคำตอบ
Teemu Leisti

4

มีคนเดินไปข้างหน้าและเขียนอัญมณีทับทิมชื่อเรนัม มันอ้างว่าได้รับพฤติกรรมคล้าย Java / C # ที่ใกล้เคียงที่สุด โดยส่วนตัวแล้วฉันยังคงเรียนรู้ทับทิมและฉันก็รู้สึกตกใจเล็กน้อยเมื่อฉันต้องการให้ชั้นเรียนเฉพาะเจาะจงมีคงที่เป็น enum ซึ่งอาจเป็นแฮชที่ไม่พบได้ง่ายผ่าน Google


ฉันไม่เคยต้องการ enum ใน Ruby สัญลักษณ์และค่าคงที่มีความหมายและแก้ปัญหาเดียวกันได้หรือไม่?
Chuck

อาจจะโยน; แต่ googling สำหรับ enum ใน ruby ​​คุณจะไม่ไปไกลขนาดนั้น มันจะแสดงผลลัพธ์ให้คุณเห็นถึงความพยายามของผู้คนในระดับที่เทียบเท่าโดยตรง ซึ่งทำให้ฉันสงสัยบางทีอาจมีบางอย่างที่ดีเกี่ยวกับการรวมแนวคิดเข้าด้วยกัน
dlamblin

@Chuck Symbols และค่าคงที่ไม่บังคับเช่นค่าต้องเป็นหนึ่งในชุดเล็ก ๆ ของค่า
David Moles

3

ทุกอย่างขึ้นอยู่กับว่าคุณใช้ Java หรือ C # enums วิธีที่คุณใช้จะกำหนดวิธีแก้ปัญหาที่คุณเลือกใน 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"}>

9
ทำไมไม่ใช้สัญลักษณ์Set[:a, :b, :c]?
Dan Rosenstark

2
วิธีปฏิบัติที่ดีกว่ามากในการใช้สัญลักษณ์ที่นี่ IMO
Collin Graves

3

เร็ว ๆ นี้เราเปิดตัวอัญมณีที่ดำเนินEnums ในรูบี ในโพสต์ของฉันคุณจะพบคำตอบสำหรับคำถามของคุณ นอกจากนี้ฉันอธิบายว่าทำไมการใช้งานของเราถึงดีกว่าที่มีอยู่เดิม (จริง ๆ แล้วมีการใช้งานของคุณลักษณะนี้ในทับทิมยังเป็นอัญมณี)


อนุญาตให้เพิ่มค่าด้วยตนเองโดยไม่ต้องระบุอย่างชัดเจน +1
หรี่ลง

3

โซลูชันอื่นกำลังใช้ 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'

2

สัญลักษณ์เป็นวิธีทับทิม อย่างไรก็ตามบางครั้งเราจำเป็นต้องพูดคุยกับรหัส C หรืออะไรบางอย่างหรือ Java ที่เปิดเผย enum บางอย่างสำหรับสิ่งต่าง ๆ


#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 ของเราเองได้


คุณใช้อักษรตัวพิมพ์ใหญ่ของคำที่สองในตัวแปร (เช่นserver_Symb) ด้วยเหตุผลเฉพาะหรือไม่? เว้นแต่จะมีเหตุผลโดยเฉพาะอย่างยิ่งมันเป็นสำนวนสำหรับตัวแปรที่จะเป็นและจะเป็นสัญลักษณ์snake_case_with_all_lower_case :lower_case
Andrew Grimm

1
@Andrew; ตัวอย่างนี้นำมาจากโลกแห่งความเป็นจริงและเอกสารประกอบโปรโตคอลเครือข่ายใช้ xxx_Yyy ดังนั้นรหัสในหลายภาษาจึงใช้แนวคิดเดียวกันเพื่อให้สามารถติดตามการเปลี่ยนแปลงข้อกำหนดได้
Jonke

1
server_Symb.each_with_index { |e,i| server_Enum[e] = i}กอล์ฟรหัสสินค้า: i = 0ไม่จำเป็นต้อง
Andrew Grimm

2

ฉันได้ดำเนินการ enums เช่นนั้น

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

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 และเชื่อมโยงกับโมเดล


:VALฉันจะไม่แนะนำวิธีนี้เพราะมันขึ้นอยู่กับการตั้งค่าด้วยตนเองคุณค่าและสร้างความมั่นใจให้คุณได้รับการสั่งซื้อที่เหมาะสมใน มันจะเป็นการดีกว่าถ้าคุณเริ่มต้นด้วยอาร์เรย์และสร้างแฮชโดยใช้.map.with_index
DaveMongoose

1
จุดที่แน่นอนคือการผูกตัวเองกับค่าที่กำหนดโดยบุคคลที่สาม มันไม่ได้เกี่ยวกับความสามารถในการขยายตัว แต่เกี่ยวกับการจัดการกับข้อ จำกัด ภายนอกที่ส่งผลกระทบต่อการคำนวณภายในขอบเขตกระบวนการของคุณ
jjk

จุดประสงค์! ในกรณีนั้นมันสมเหตุสมผลที่จะระบุค่า แต่ฉันอยากจะทำการค้นหาแบบย้อนกลับด้วย.keyหรือ.invertมากกว่า:VALคีย์ ( stackoverflow.com/a/10989394/2208016 )
DaveMongoose

ใช่นั่นคือ (กลับมาที่คุณ) จุดยุติธรรม ทับทิมของฉันไม่ค่อยสวยและเทอะทะ จะ def ใช้keyหรือinvert
jjk

1

คนส่วนใหญ่ใช้สัญลักษณ์ (นั่นคือ:foo_barไวยากรณ์) มันเป็นค่าทึบแสงที่ไม่เหมือนใคร สัญลักษณ์ไม่ได้เป็นของประเภท enum ใด ๆ ดังนั้นพวกเขาจึงไม่ได้เป็นตัวแทนที่ซื่อสัตย์ของประเภท enum ของ 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

เอาท์พุท:

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


to_enumให้ enumera torในขณะที่enumใน C # / Java sense เป็น 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)

เอาท์พุท:

GOOD

1

บางครั้งสิ่งที่ฉันต้องการก็คือสามารถดึงคุณค่าของ enum และระบุชื่อของมันคล้ายกับโลกของจาวา

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 และวิโอล่าหาได้ฟรีในทุก enums ที่กำหนดไว้ ตัวอย่างเช่น. get_all_names และสิ่งต่าง ๆ เช่นนั้น


0

อีกวิธีหนึ่งคือการใช้ระดับทับทิมกับกัญชาที่มีชื่อและค่าตามที่อธิบายไว้ในต่อไปนี้โพสต์บล็อก RubyFleebie สิ่งนี้ช่วยให้คุณสามารถแปลงระหว่างค่าและค่าคงที่ได้อย่างง่ายดาย (โดยเฉพาะถ้าคุณเพิ่มเมธอดคลาสเพื่อค้นหาชื่อสำหรับค่าที่กำหนด)


0

ฉันคิดว่าวิธีที่ดีที่สุดในการใช้การแจงนับเช่นประเภทคือมีสัญลักษณ์เนื่องจากพฤติกรรมค่อนข้างเป็นจำนวนเต็ม (เมื่อพูดถึง performace, object_id ใช้เพื่อทำการเปรียบเทียบ); คุณไม่จำเป็นต้องกังวลเกี่ยวกับการจัดทำดัชนีและพวกเขาดูเรียบร้อยในรหัส xD ของคุณ


0

อีกวิธีในการเลียนแบบ enum ที่มีการจัดการความเท่าเทียมกันอย่างสม่ำเสมอ อนุญาตให้ enums เปิด (เหมือนสัญลักษณ์) และปิด (กำหนดไว้ล่วงหน้า) enums

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.