Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

วิธียกวัตถุใด ๆ เป็นข้อยกเว้นของ Ruby

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

raise "hello"
raise RuntimeError, "hello"
raise RuntimeError.new("hello")

# ...all of the above result in "RuntimeError: hello"

เป็นเรื่องที่ดี แต่จะเกิดอะไรขึ้นเมื่อฉันต้องการแจ้งเรื่องอื่นที่ไม่ใช่ข้อยกเว้น จะทำอย่างไรถ้าฉันต้องการเพิ่มจำนวน รูบี้ไม่ยอมให้ฉัน ฉันได้รับข้อความแสดงข้อผิดพลาดดังนี้:

raise 1
# TypeError: exception class/object expected

ตอนนี้ข้อความนี้อาจทำให้คุณเชื่อว่าการยกต้องการคลาส/อ็อบเจ็กต์ข้อยกเว้นเป็นพารามิเตอร์ แต่มันไม่ถูกต้อง!

แนะนำexception วิธีการ

หากคุณ raise foo วิธีการยกไม่คาดหวังว่า foo จะเป็นวัตถุข้อยกเว้น คาดว่าจะได้รับวัตถุยกเว้นเมื่อใดก็ตามที่เรียก foo.exception .

สิ่งที่ต้องจำไว้คือคุณสามารถส่งผ่านอะไรก็ได้ที่จะเพิ่ม ตราบใดที่มีวิธีที่เรียกว่าข้อยกเว้นที่ส่งกลับข้อยกเว้น

ดังนั้น ถ้าคุณต้องการ คุณสามารถเรียนหมายเลขของ Monkeypatch ruby ​​เพื่อให้คุณสามารถเพิ่มจำนวนได้ หน้าตาจะประมาณนี้:

class Fixnum
  def exception
    RuntimeError.new("I'm number: #{ self }")
  end
end

raise 42
# ...results in raise_number.rb:7:in `<main>': I'm number: 42 (RuntimeError)

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

ตัวอย่างที่นำไปใช้ได้จริง

สมมติว่าฉันต้องการอ่านบรรทัดข้อมูลจาก IO บางประเภท อาจเป็นเครือข่าย IO อาจเป็นไฟล์ มันไม่สำคัญจริงๆ ฉันแค่ต้องการอ่านข้อมูลและดูว่าข้อมูลถูกต้องหรือไม่

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

# These three classes represent different kinds of IO with different exceptions.
class NetworkConnection
  ...
  def exception
    NetworkConnectionError.new(url: url, ...)
  end
end

class LocalFile
  ...
  def exception
    FileError.new(path: path, ...)
  end
end

class UnixPipe
  ...
  def exception
    PipeError.new(...)
  end
end

def read_all(*items)
  items.each do |item|
    if item.readline != "foo"
      # We raise the item, which causes the appropriate exception class to be used. 
      raise item
    end
  end
end

read_all(NetworkConnection.new(url: "example.com"), LocalFile.new("/something"), UnixPipe.new)