2009-04-02 36 views
12

Hãy nói rằng tôi có một lớp Ruby:Có thể có class.property = x trả về một cái gì đó khác với x?

class MyClass 
    def self.property 
    return "someVal" 
    end 

    def self.property=(newVal) 
    # do something to set "property" 
    success = true 

    return success # success is a boolean 
    end 
end 

Nếu tôi cố gắng và làm MyClass.property=x, giá trị sinh lợi của cả tuyên bố luôn là x. Nó là một quy ước trong rất nhiều ngôn ngữ dựa trên C/lấy cảm hứng để trả về một giá trị "thành công" boolean - có thể làm điều này cho một setter bằng cách sử dụng "bằng cú pháp" trong Ruby không?

Hơn nữa - nếu điều này là không thể, tại sao không? Có bất kỳ nhược điểm nào có thể hiểu được để cho phép hoạt động "bằng setter" trả về một giá trị không?

Trả lời

11

Một nhược điểm là bạn sẽ phá vỡ các ngữ nghĩa phân xích:

$ irb 
irb(main):001:0> x = y = 3 
=> 3 
irb(main):002:0> p x 
3 
=> nil 
irb(main):003:0> p y 
3 
=> nil 
irb(main):004:0> 

xem xét:

x = MyClass.property = 3 

Sau đó x sẽ mất true nếu điều này làm việc như bạn đã mong đợi (phải associativity). Đó có thể là một bất ngờ cho những người sử dụng giao diện của bạn và được sử dụng cho các ngữ nghĩa điển hình.

Bạn cũng đã cho tôi suy nghĩ về nhiệm vụ song song, ví dụ:

x, y = 1, 2 

Rõ ràng giá trị trả về từ biểu thức đó là implementation specific ... Tôi đoán tôi sẽ không được chaining được kết tập song song :)

Câu hỏi hay!

+0

đầu tiên tôi đọc là "nếu bạn đã định nghĩa một accessor như thế, bạn sẽ phá vỡ các ngữ nghĩa phân xích ". Vì câu trả lời của rampion cho thấy rõ ràng, không phải như vậy: giá trị trả về của một phương pháp thẩm định thường bị bỏ qua. Những gì Martin đang nói là "ngôn ngữ không cho phép bạn làm điều đó, nếu nó đã làm, bạn sẽ phá vỡ các ngữ nghĩa phân định chuỗi". –

5

Tôi không phải là chuyên gia về Ruby nhưng tôi muốn nói không vì trường hợp đó tôi sợ. Một setter thuộc tính là chỉ có ở đó để thiết lập giá trị của một trường riêng, không có bất kỳ tác dụng phụ nào như trả về các mã kết quả.

Nếu bạn muốn chức năng đó sau đó quên setter và viết một phương pháp mới gọi là TrySetProperty hoặc một cái gì đó mà cố gắng để thiết lập các tài sản và trả về một boolean.

7

Giống như Martin nói, điều này sẽ phá vỡ chuỗi chỉ định.

Cách thức phân công ruby ​​được xác định để làm việc mở rộng MyClass.property = 3 tương đương với (lambda { |v| MyClass.send('property=', v); v })[3] (không thực sự, nhưng điều này cho thấy cách hoạt động của chuỗi). Giá trị trả lại của nhiệm vụ luôn là giá trị được gán.

Nếu bạn muốn xem kết quả của phương pháp MyClass#property= của bạn, sau đó sử dụng #send:

irb> o = Object.new 
=> #<Object:0x15270> 
irb> def o.x=(y) 
irb> @x = y+1 
irb> puts "y = #{y}, @x = #@x" 
irb> true 
irb> end 
=> nil 
irb> def o.x 
irb> puts "@x = #@x" 
irb> @x 
irb> end 
=> nil 
irb> o.x = 4 
y = 4, @x = 5 
=> 4 
irb> o.x 
@x = 5 
=> 5 
irb> o.send('x=', 3) 
y = 3, @x = 4 
=> true 

Tuy nhiên, cách ruby ​​để làm điều này là với trường hợp ngoại lệ - nếu họ gặp khó khăn trong nhiệm vụ, nâng cao một ngoại lệ. Sau đó tất cả invokers phải xử lý nó nếu có điều gì sai, không giống như một giá trị trả về, có thể dễ dàng bỏ qua:

# continued from above... 
irb> def o.x=(y) 
irb> unless y.respond_to? :> and (y > 0 rescue false) 
irb>  raise ArgumentError, 'new value must be > 0', caller 
irb> end 
irb> @x = y + 1 
irb> puts "y = #{y}, @x = #@x" 
irb> end 
=> nil 
irb> o.x = 4 
y = 4, @x = 5 
=> 4 
irb> o.x = 0 
ArgumentError: new value must be > 0 
    from (irb):12 
    from :0 
irb> o.x = "3" 
ArgumentError: new value must be > 0 
    from (irb):13 
    from :0 
irb> o.x 
@x = 5 
=> 5 
Các vấn đề liên quan