2010-03-15 18 views
14

Tôi bị kẹt. Tôi đang cố gắng để xác định động một phương pháp lớp và tôi không thể quấn đầu của tôi xung quanh mô hình metaclass ruby. Xem xét các lớp sau:Ruby metaclass madness

class Example 

    def self.meta; (class << self; self; end); end 

    def self.class_instance; self; end 

end 

Example.class_instance.class # => Class 
Example.meta.class   # => Class 

Example.class_instance == Example  # => true 
Example.class_instance == Example.meta # => false 

Rõ ràng cả hai phương thức đều trả về một thể hiện của Lớp. Nhưng hai trường hợp này không giống nhau. Họ cũng có tổ tiên khác nhau:

Example.meta.ancestors   # => [Class, Module, Object, Kernel] 
Example.class_instance.ancestors # => [Example, Object, Kernel] 

Điểm khác biệt trong việc tạo sự khác biệt giữa metaclass và cá thể lớp học là gì?

Tôi đã tìm ra, rằng tôi có thể send :define_method với metaclass để xác định động một phương pháp, nhưng nếu tôi cố gắng gửi nó đến cá thể lớp, nó sẽ không hoạt động. Ít nhất tôi có thể giải quyết vấn đề của mình, nhưng tôi vẫn muốn hiểu tại sao nó lại hoạt động theo cách này.

Update Mar 15, 2010 13:40

Are các giả định sau đúng.

  • Nếu tôi có phương pháp thể hiện gọi self.instance_eval và xác định phương thức, nó sẽ chỉ ảnh hưởng đến trường hợp cụ thể của lớp đó.
  • Nếu tôi có một phương thức cá thể gọi self.class.instance_eval (sẽ giống như gọi class_eval) và định nghĩa một phương thức, nó sẽ ảnh hưởng đến tất cả các cá thể của lớp cụ thể đó dẫn đến một phương thức mới.
  • Nếu tôi có một phương thức lớp gọi instance_eval và định nghĩa một phương thức, nó sẽ dẫn đến một phương thức thể hiện mới cho tất cả các cá thể.
  • Nếu tôi có một phương thức lớp gọi instance_eval trên lớp meta/eigen và định nghĩa một phương thức, nó sẽ dẫn đến một phương thức lớp.

Tôi nghĩ điều đó bắt đầu có ý nghĩa với tôi. Nó chắc chắn sẽ giới hạn khả năng của bạn nếu tự bên trong một phương thức lớp sẽ trỏ đến lớp riêng. Nếu không, nó sẽ không thể xác định một phương thức thể hiện từ bên trong một phương thức lớp. Đúng không?

Trả lời

11

Xác định một phương pháp singleton động rất đơn giản khi bạn sử dụng instance_eval:

Example.instance_eval{ def square(n); n*n; end } 
Example.square(2) #=> 4 
# you can pass instance_eval a string as well. 
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27 

Đối với sự khác biệt trên, bạn đang bối rối 2 điều:

Lớp meta xác định bởi bạn, là những gì gọi Cộng đồng Ruby là lớp singelton hoặc lớp eigen. Lớp singleton đó là lớp mà bạn có thể thêm các phương thức lớp (singleton) vào.

Đối với cá thể lớp bạn đang cố gắng xác định bằng cách sử dụng phương thức class_instance, không có gì ngoài chính lớp đó, để chứng minh điều đó, chỉ cần thử thêm phương thức thể hiện vào lớp Example và kiểm tra xem phương thức class_instance được bạn xác định lớp Example bản thân bằng cách kiểm tra sự tồn tại của phương pháp đó:

class Example 
    def self.meta; (class << self; self; end); end 
    def self.class_instance; self; end 
    def hey; puts hey; end 
end 

Example.class_instance.instance_methods(false) #=> ['hey'] 

Dù sao để tóm tắt cho bạn, khi bạn muốn thêm các phương pháp lớp học, chỉ cần thêm chúng vào đó lớp meta. Đối với phương pháp class_instance là vô ích, chỉ cần loại bỏ nó.

Dù sao tôi đề nghị bạn đọc this post để nắm bắt một số khái niệm về hệ thống phản chiếu của Ruby.

CẬP NHẬT

tôi đề nghị bạn đọc bài đăng này đẹp: Fun with Ruby's instance_eval and class_eval, Thật không may class_evalinstance_eval là khó hiểu vì họ bằng cách nào đó làm việc chống lại tên tuổi của họ!

Use ClassName.instance_eval to define class methods. 

Use ClassName.class_eval to define instance methods. 

Bây giờ trả lời giả định của bạn:

Nếu tôi có một phương pháp dụ mà cuộc gọi self.instance_eval và định nghĩa một phương pháp , nó sẽ chỉ ảnh hưởng đến dụ cụ thể của lớp đó.

yes:

class Foo 
    def assumption1() 
    self.instance_eval("def test_assumption_1; puts 'works'; end") 
    end 
end 

f1 = Foo.new 
f1.assumption1 
f1.methods(false) #=> ["test_assumption_1"] 
f2 = Foo.new.methods(false) #=> [] 

Nếu tôi có một phương pháp dụ mà cuộc gọi self.class.instance_eval (mà sẽ được giống như gọi class_eval) và định nghĩa một method nó sẽ ảnh hưởng đến tất cả các trường hợp của lớp cụ thể này dẫn đến phương pháp thể hiện mới.

không instance_eval trong bối cảnh đó sẽ xác định phương pháp singleton (không những chẳng hạn) trên chính lớp:

class Foo 
    def assumption2() 
    self.class.instance_eval("def test_assumption_2; puts 'works'; end") 
    end 
end 

f3 = Foo.new 
f3.assumption2 
f3.methods(false) #=> [] 
Foo.singleton_methods(false) #=> ["test_assumption_2"] 

Cho rằng để làm việc thay thế instance_eval với class_eval trên.

Nếu tôi có một phương pháp học mà gọi instance_eval và định nghĩa một method nó sẽ dẫn đến một phương pháp thể hiện mới cho tất cả các trường.

Nope:

class Foo 
    instance_eval do 
    def assumption3() 
     puts 'works' 
    end 
    end 
end 

Foo.instance_methods(false) #=> [] 

Foo.singleton_methods(false) #=> ["assumption_3"] 

Điều đó sẽ làm cho phương pháp singleton, không phương pháp dụ. Để làm việc đó, hãy thay thế instance_eval bằng class_eval ở trên.

Nếu tôi có một phương pháp học mà gọi instance_eval trên lớp meta/eigen và định nghĩa một method nó sẽ gây ra một phương pháp học.

cũng không, điều đó sẽ tạo ra những thứ phức tạp, vì nó sẽ thêm phương thức đơn vào lớp singleton, tôi không nghĩ rằng sẽ có bất kỳ cách sử dụng thực tế nào.

+0

Để biết thêm thông tin về _why_ một 'def' bên trong một' instance_eval' định nghĩa các phương thức lớp, hãy xem bài viết này http://yugui.jp/articles/846 – horseyguy

+0

Cảm ơn rất nhiều cho đến nay. Tôi cập nhật câu hỏi của mình. Bạn có phiền khi xem nó không? – t6d

+0

Được cập nhật để trả lời câu hỏi trong bản cập nhật của bạn. – khelll

5

Nếu bạn xác định phương thức trên lớp, nó có thể được gọi trên đối tượng của nó. Đây là phương thức ví dụ .

class Example 
end 

Example.send :define_method, :foo do 
    puts "foo" 
end 

Example.new.foo 
#=> "foo" 

Nếu bạn định nghĩa một phương thức trên một metaclass, nó có thể được gọi vào lớp. Điều này tương tự như khái niệm về phương thức lớp học hoặc phương pháp tĩnh bằng các ngôn ngữ khác.

class Example 
    def self.metaclass 
    class << self 
     self 
    end 
    end 
end 

Example.metaclass.send :define_method, :bar do 
    puts "bar" 
end 

Example.bar 
#=> "bar" 

Các lý do rằng metaclasses tồn tại là vì bạn có thể làm điều này trong Ruby:

str = "hello" 
class << str 
    def output 
    puts self 
    end 
end 

str.output 
#=> "hello" 

"hi".output 
# NoMethodError 

Như bạn có thể thấy, chúng ta định nghĩa một phương pháp mà chỉ dành cho những một thể hiện của một Chuỗi. Thứ mà chúng tôi đã xác định phương thức này được gọi là metaclass. Trong chuỗi tra cứu phương thức, metaclass được truy cập trước khi tìm kiếm lớp của đối tượng.

Nếu chúng ta thay thế các đối tượng kiểu String với một đối tượng kiểu Class, bạn có thể tưởng tượng được tại sao điều này có nghĩa là chúng tôi chỉ xác định một phương pháp trên một lớp cụ, không phải trên tất cả các lớp học.

Sự khác biệt giữa ngữ cảnh hiện tại và self là tinh tế, bạn có thể read more nếu bạn quan tâm.