2009-09-15 23 views
19

Có sự khác biệt nào nếu bạn định nghĩa Foo với instance_eval:. . .Có phải 'năng suất tự' giống như instance_eval không?

class Foo 
    def initialize(&block) 
     instance_eval(&block) if block_given? 
    end 
    end 

. . . hoặc với 'năng suất tự':

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 

Trong cả hai trường hợp, bạn có thể làm điều này:

x = Foo.new { def foo; 'foo'; end } 
x.foo 

Vì vậy, 'yield self' có nghĩa là khối sau Foo.new luôn được đánh giá trong bối cảnh của Lớp Foo.

Điều này có đúng không?

Trả lời

14

Hai đoạn mã của bạn làm những việc rất khác nhau. Bằng cách sử dụng instance_eval bạn đang đánh giá khối trong ngữ cảnh của đối tượng của bạn. Điều này có nghĩa là việc sử dụng def sẽ định nghĩa các phương thức trên đối tượng đó. Nó cũng có nghĩa là việc gọi một phương thức mà không có người nhận bên trong khối sẽ gọi nó trên đối tượng của bạn.

Khi bạn tự tự mình chuyển mình như một đối số cho khối, nhưng vì khối của bạn không nhận bất kỳ đối số nào, nó chỉ bị bỏ qua. Vì vậy, trong trường hợp này năng suất tự làm điều tương tự như năng suất không có gì. Các def ở đây cư xử chính xác như một def bên ngoài khối sẽ, năng suất tự không thực sự thay đổi những gì bạn xác định phương pháp trên. Những gì bạn có thể làm là:

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 
x = Foo.new {|obj| def obj.foo() 'foo' end} 
x.foo 

Sự khác biệt đối với instance_eval là bạn phải chỉ định người nhận một cách rõ ràng.

Chỉnh sửa để làm rõ:

Trong phiên bản có năng suất, obj trong khối sẽ là đối tượng được mang lại, mà trong trường hợp này là là dụ Foo mới được tạo ra. Trong khi bản thân sẽ có cùng giá trị bên ngoài khối. Với phiên bản instance_eval self bên trong khối sẽ là cá thể Foo mới được tạo.

+0

Trong "Chỉnh sửa để làm rõ" của bạn, bạn không có nghĩa là bản thân được mang đến obj trong khối? Có lẽ tôi chỉ đọc nó theo một cách khác nhưng tôi thấy đối tượng đang được khởi tạo, tự được mang đến khối như 'obj' và sau đó bên trong khối, phương thức foo được định nghĩa trên bản thân thông qua obj. – uzo

+1

Tôi khá chắc chắn, chúng tôi có ý nghĩa tương tự. Tôi đã viết "trường hợp Foo mới được tạo ra" vì tự bên trong phương thức khởi tạo (là cá thể Foo mới được tạo) không giống như tự bên trong khối và nếu bạn chỉ nói "tự", bạn không rõ ý bạn là gì. – sepp2k

4

Bạn chỉ có thể thả tự từ khóa

class Foo 
    def initialize 
    yield if block_given? 
    end 
end 

Update từ bình luận

Sử dụng năng suất có một chút mới để hương vị của tôi, đặc biệt khi sử dụng bên ngoài IRB.

Tuy nhiên có một sự khác biệt lớn và có ý nghĩa giữa instance_eval cách tiếp cận và năng suất cách tiếp cận, kiểm tra đoạn này:

class Foo 
    def initialize(&block) 
    instance_eval(&block) if block_given? 
    end 
end 
x = Foo.new { def foo; 'foo'; end }    
#=> #<Foo:0xb800f6a0>            
x.foo #=> "foo"               
z = Foo.new #=> #<Foo:0xb800806c>            
z.foo #=>NoMethodError: undefined method `foo' for #<Foo:0xb800806c> 

Kiểm tra này là tốt:

class Foo2 
    def initialize 
    yield if block_given? 
    end 
end 
x = Foo2.new { def foo; 'foo'; end } #=> #<Foo:0xb7ff1bb4> 
x.foo #=> private method `foo' called for #<Foo2:0xb8004930> (NoMethodError) 
x.send :foo => "foo" 
z = Foo.new #=> #<Foo:0xb800806c> 
z.send :foo => "foo" 

Như bạn có thể thấy sự khác biệt là trước đây là thêm một phương pháp singleton foo cho đối tượng đang được khởi tạo, whil e sau này là thêm một phương thức riêng cho tất cả các cá thể của lớp Object.

+2

"Như bạn có thể thấy sự khác biệt là người trước đây đang thêm một phương thức đơn lẻ foo vào đối tượng đang được khởi tạo, trong khi sau đó thêm phương thức đó vào tất cả các phiên bản của lớp Foo2" Đối tượng (và trong ruby ​​thực (như trái ngược với irb) nó sẽ thêm nó như là một phương thức riêng để bạn không thể làm x.foo hoặc z.foo, chỉ foo). Nói cách khác, def hoạt động chính xác như thể bạn đã viết nó bên ngoài khối (trừ khi phương thức không mang lại tất nhiên, trong trường hợp đó không có gì xảy ra). – sepp2k

+0

Bạn hoàn toàn đúng, cảm ơn – khelll

+0

Trong Ruby 1.9, bạn không nhận được định nghĩa phương thức riêng tư. Thay vào đó, x = Foo2.new {def foo; 'foo'; end} sẽ xác định phương thức 'foo' trên Object (như sepp2k đã nói). Bạn có thể thấy điều này bằng cách nói: x.methods (false) .grep/foo/ # => [] Object.new.foo # => "foo" –

7

Chúng khác nhau. yield(self) không thay đổi giá trị của self bên trong khối, trong khi instance_eval(&block).

class Foo 
    def with_yield 
    yield(self) 
    end 

    def with_instance_eval(&block) 
    instance_eval(&block) 
    end 
end 

f = Foo.new 

f.with_yield do |arg| 
    p self 
    # => main 
    p arg 
    # => #<Foo:0x100124b10> 
end 

f.with_instance_eval do |arg| 
    p self 
    # => #<Foo:0x100124b10> 
    p arg 
    # => #<Foo:0x100124b10> 
end 
+0

Thứ hai 'p arg' nên in' nil', không phải '# '. – sepp2k

+0

Trong 1.8.7, thể hiện Foo được in. Tôi nghĩ rằng nó sẽ là nil là tốt, không chắc chắn lý do tại sao nó không phải là. –

+0

Trong 1,9 nil được in. Tôi không thể nhìn thấy bất kỳ lời giải thích cho 1.8.7 in Foo dụ. Bạn có chắc là bạn không đọc nhầm kết quả đầu ra không? – sepp2k

Các vấn đề liên quan