Tại sao eigenclass không tương đương với self.class, khi nó trông rất giống nhau?


83

Tôi đã bỏ sót bản ghi nhớ ở đâu đó, và tôi hy vọng bạn sẽ giải thích điều này cho tôi.

Tại sao eigenclass của một đối tượng khác với self.class?

class Foo
  def initialize(symbol)
    eigenclass = class << self
      self
    end
    eigenclass.class_eval do
      attr_accessor symbol
    end
  end
end

Phương thức logic của tôi đánh đồng eigenclass với class.selfkhá đơn giản:

class << selflà một cách khai báo các phương thức của lớp, chứ không phải là các phương thức thể hiện. Đó là một lối tắt đến def Foo.bar.

Vì vậy, trong tham chiếu đến đối tượng lớp, trả về selfphải giống với self.class. Điều này là do class << selfsẽ thiết lập selfđể Foo.classcho định nghĩa của phương pháp lớp / thuộc tính.

Tôi chỉ bối rối thôi sao? Hay, đây là một thủ thuật lén lút của lập trình meta Ruby?

Câu trả lời:


122

class << selfkhông chỉ là một cách khai báo các phương thức của lớp (mặc dù nó có thể được sử dụng theo cách đó). Có thể bạn đã thấy một số cách sử dụng như:

class Foo
  class << self
    def a
      print "I could also have been defined as def Foo.a."
    end
  end
end

Điều này hoạt động, và tương đương với def Foo.a, nhưng cách nó hoạt động hơi tinh tế. Bí mật là self, trong bối cảnh đó, tham chiếu đến đối tượng Foo, có lớp là một lớp con ẩn danh, duy nhất của Class. Lớp con này được gọi là Foo's eigenclass . Vì vậy, def atạo ra một phương pháp mới gọi là atrong Foo's eigenclass, truy cập bằng cú pháp gọi phương thức bình thường: Foo.a.

Bây giờ chúng ta hãy xem xét một ví dụ khác:

str = "abc"
other_str = "def"

class << str
  def frob
    return self + "d"
  end
end

print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str

Ví dụ này giống với ví dụ cuối cùng, mặc dù ban đầu có thể khó nói. frobđược định nghĩa, không phải trên Stringlớp, mà trên lớp eigenclass của str, một lớp con ẩn danh duy nhất của String. Vì vậy, strcó một frobphương pháp, nhưng các trường hợp Stringnói chung thì không. Chúng tôi cũng có thể có các phương thức được ghi đè của Chuỗi (rất hữu ích trong một số trường hợp thử nghiệm phức tạp).

Bây giờ chúng tôi được trang bị để hiểu ví dụ ban đầu của bạn. FooPhương thức khởi tạo của Inside , selfkhông đề cập đến lớp Foo, mà là một số trường hợp cụ thể của Foo. Eigenclass của nó là một lớp con của Foo, nhưng nó không phải là Foo; nó không thể được, hoặc nếu không thì thủ thuật chúng ta đã thấy trong ví dụ thứ hai không thể hoạt động. Vì vậy, để tiếp tục ví dụ của bạn:

f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)

f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.

Hi vọng điêu nay co ich.


Vì vậy, sau đó, mỗi cá thể là một lớp con ẩn danh của lớp đã tạo?
Robert K

21
Lớp của mỗi cá thể là một lớp con ẩn danh của lớp đã tạo. lớp của f1 là lớp con ẩn danh của Foo, lớp của Foo là lớp con ẩn danh của Class.
David Seiler

6
câu trả lời hay :) rất nhiều người không hiểu điều này rõ ràng như bạn làm.
Horseyguy

3
Về mặt khái niệm, eigenclass của f1 khác với trường hợp thực của f1 như thế nào. Nếu f1 là trường hợp duy nhất có quyền truy cập vào các phương thức của lớp eigen của nó, thì sự phân biệt giữa f1 và loại eigenclass của nó có bị phá vỡ không?
elju

1
@elju Vâng, kinda. Sự khác biệt thực sự quan trọng là giữa "Foo" và "eigenclass của f1"; nếu bạn có điều đó, bạn có thể ổn.
David Seiler

46

Câu trả lời đơn giản nhất: eigenclass không thể được khởi tạo.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class

bạn có thể chỉ có 1 điểm trên trang web này, nhưng tôi thích bạn và phong cách của bạn.
Horseyguy

Đồng ý w / lan can; đây là một câu trả lời rất lớn
Christopher Scott

3
Đây là một nhận xét cực kỳ sâu sắc và hữu ích mà IFF đã đọc câu trả lời của @ DavidSeiler ở trên.
Jazz

Theo quyền lực ở đây là demo'ing ngoại lệ được nêu ra.
New Alexandria

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.