2015-08-02 10 views
9

Po pierwsze, rozumiem, że to pytanie nie ma zastosowania w świecie rzeczywistym, jestem po prostu ciekawy.Wyszukiwanie metodą singleton class

Wyobraźmy sobie, że mamy klasę z metodą jednoelementowy:

class Foo 
    def self.bar 
    end 
end 

Jeśli nazywamy Foo.bar, będzie to pierwszy wyszukiwania dla sposobu w singleton klasy każdego przodka Foo, a następnie będzie wyglądać w klasie odwołuje się do metody .class i jej przodków. Możemy potwierdzić, że z Foo.singleton_class.ancestors, która zwraca:

[#<Class:Foo>, #<Class:Object>, #<Class:BasicObject>, 
Class, Module, Object, Kernel, BasicObject] 

Ale co się stanie, jeśli mamy zagnieżdżone klasy singleton, jak:

class Foo 
    class << self 
    class << self 
     def bar 
     end 
    end 
    end 
end 

Jeśli nazywamy Foo.singleton_class.singleton_class.ancestors, zwraca:

[#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, 
#<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, 
#<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 

Nie rozumiem, w jaki sposób zorganizowana jest ta hierarchia.

+0

Zakładasz, że jest w ogóle zorganizowana. Jesteś pewien, że to jest/powinno być zorganizowane? –

+0

Być może mógłbyś wyjaśnić swoje pytanie? Czy pytasz, gdzie jest zdefiniowana metoda paska w twoim drugim przykładzie? – saghaulor

+0

Metoda paska jest zdefiniowana. Pytam, jak zorganizowana jest hierarchia dziedziczenia w drugim przykładzie. –

Odpowiedz

16

wiele z tych wyjaśnień jest oparta na How Ruby Method Dispatch Works James Coglan, trochę z Ruby Hacking Guide, a tylko smidge z source.

Aby rozpocząć z Podsumowując, pochodzenie wygląda następująco:

              +----------------+ 
                  |    | 
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>   | 
|       ^      ^    | 
|        |       |    | 
|       Class ~~~~~~~~~~~~~~~> #<Class:Class>   | 
|       ^      ^    | 
|        |       |    | 
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> | 
| ^     ^      ^    | 
|  |  Kernel   |       |    | 
|  |  ^   |       |    | 
|  |   |    | +-----------------------|----------------+ 
|  +-----+----+    | |      | 
|   |     | v      | 
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>> 
      ^    ^      ^
      |     |       | 
      Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>> 

---> Parent 
~~~> Singleton class 

Zacznijmy od początku i zbudować. BasicObject jest źródłem wszystkiego - jeśli sprawdzisz BasicObject.superclass, otrzymasz nil. BasicObject jest również instancją Class. Tak, to jest okrągłe, i jest special case in the code, aby sobie z tym poradzić.Kiedy A jest instancją B, A.singleton_class jest dzieckiem B, więc mamy to:

      Class 
          ^
          | 
BasicObject ~~~~~> #<Class:BasicObject> 

Object dziedziczy z BasicObject. Gdy A dziedziczy po B, A jest dzieckiem o numerach i , które jest potomkiem B.singleton_class. Object zawiera także Kernel. Kiedy A zawiera B, B jest wstawiany jako pierwszy przodek A (po A sam w sobie, ale przed A.superclass).

      Class 
          ^
          | 
BasicObject ~~~~~> #<Class:BasicObject 
    ^     ^
    |  Kernel   | 
    |  ^   | 
    |   |    | 
    +-----+----+    | 
      |     | 
     Object ~~~~~~> #<Class:Object> 

Kernel jest wystąpienie Module. Jest to jedyny przypadek Module, którego zobaczymy, a jego klasa singletona nie pojawia się w żadnym łańcuchu przodków, więc nie wyjdę poza to.

Teraz przejdziemy do Foo, która dziedziczy po Object (chociaż nie trzeba pisać < Object). Możemy już dowiedzieć się, co to jest Foo i jego klasa singleton.

      Class 
          ^
          | 
BasicObject ~~~~~> #<Class:BasicObject> 
    ^     ^
    |  Kernel   | 
    |  ^   | 
    |   |    | 
    +-----+----+    | 
      |     | 
     Object ~~~~~~> #<Class:Object> 
     ^    ^
      |     | 
     Foo ~~~~~~~~> #<Class:Foo> 

Teraz Class dziedziczy z Module i Module dziedziczy z Object, więc dodać Module i odpowiednie klasy Singleton. Ponieważ Module < Object i Object < BasicObject i BasicObject.instance_of?(Class) jest to miejsce, w którym rysunek staje się nieco funky. Pamiętaj, że po prostu przestajesz przechodzić w górę za każdym razem, gdy trafisz na BasicObject.

              +----------------+ 
                  |    | 
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>   | 
|       ^      ^    | 
|        |       |    | 
|       Class ~~~~~~~~~~~~~~~> #<Class:Class>   | 
|       ^           | 
|        |           | 
| BasicObject ~~~~~> #<Class:BasicObject>         | 
| ^     ^           | 
|  |  Kernel   |           | 
|  |  ^   |           | 
|  |   |    | +----------------------------------------+ 
|  +-----+----+    | | 
|   |     | v 
+-------> Object ~~~~~~> #<Class:Object> 
      ^    ^
      |     | 
      Foo ~~~~~~~~> #<Class:Foo> 

Ostatni krok. Każde wystąpienie Class ma singleton_class (choć nie będzie instancji, dopóki nie jest potrzebne, lub potrzebujesz więcej RAM). Wszystkie nasze klasy singleton są instancjami Class, więc mają klasy singleton. Uważaj na to zdanie: rodzic klasy singleton jest rodzicem klasy singleton klasy. Nie wiem, czy istnieje zwięzły sposób na stwierdzenie, że jeśli chodzi o systemy typu, a Ruby source mówi, że po prostu robi to dla spójności w każdym przypadku. Tak więc, jeśli poprosisz o Foo.singleton_class.singleton_class, język szczęśliwie obliguje cię i propaguje niezbędne rodziców w górę, prowadząc ostatecznie do:

              +----------------+ 
                  |    | 
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module>   | 
|       ^      ^    | 
|        |       |    | 
|       Class ~~~~~~~~~~~~~~~> #<Class:Class>   | 
|       ^      ^    | 
|        |       |    | 
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> | 
| ^     ^      ^    | 
|  |  Kernel   |       |    | 
|  |  ^   |       |    | 
|  |   |    | +-----------------------|----------------+ 
|  +-----+----+    | |      | 
|   |     | v      | 
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>> 
      ^    ^      ^
      |     |       | 
      Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>> 

Jeśli zaczniesz z dowolnego węzła na tym wykresie i przesuw głębokości pierwszego, od prawej do lewej (i zatrzymaj się na BasicObject, otrzymasz łańcuch przodków węzła, dokładnie tak, jak chcieliśmy, i stworzyliśmy go z pewnych podstawowych aksjomatów, więc możemy po prostu móc mu zaufać, ponieważ brakuje zaufania, istnieje kilka interesujących sposobów, aby zweryfikuj strukturę dalej:

Spróbuj spojrzeć na node.singleton_class.ancestors - node.ancestors dla dowolnego węzła grafu, co daje nam przodków klasy singleton, którzy nie są przodkami sam węzeł, który eliminuje niektóre z mylących nadmiarowości na liście.

> Foo.singleton_class.singleton_class.ancestors - Foo.singleton_class.ancestors 
=> [#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, 
    #<Class:Class>, #<Class:Module>] 

Możesz również zweryfikować jednego z rodziców z node.superclass.

> Foo.singleton_class.singleton_class.superclass 
=> #<Class:#<Class:Object>> 

I można nawet sprawdzić, czy obiekt jest tożsamość wszystko spójne, więc nie są anonimowe klas pojawiały się w każdym miejscu, bez określonego stosunku do siebie.

> def ancestor_ids(ancestors) 
> ancestors.map(&:object_id).zip(ancestors).map{|pair| pair.join("\t")} 
> end 

> puts ancestor_ids(Foo.ancestors) 
70165241815140 Foo 
70165216040500 Object 
70165216040340 Kernel 
70165216040540 BasicObject 

> puts ancestor_ids(Foo.singleton_class.ancestors) 
70165241815120 #<Class:Foo> 
70165216039400 #<Class:Object> 
70165216039380 #<Class:BasicObject> 
70165216040420 Class 
70165216040460 Module 
70165216040500 Object # Same as Foo from here down 
70165216040340 Kernel 
70165216040540 BasicObject 

> puts ancestor_ids(Foo.singleton_class.singleton_class.ancestors) 
70165241980080 #<Class:#<Class:Foo>> 
70165215986060 #<Class:#<Class:Object>> 
70165215986040 #<Class:#<Class:BasicObject>> 
70165216039440 #<Class:Class> 
70165216039420 #<Class:Module> 
70165216039400 #<Class:Object> # Same as Foo.singleton_class from here down 
70165216039380 #<Class:BasicObject> 
70165216040420 Class 
70165216040460 Module 
70165216040500 Object 
70165216040340 Kernel 
70165216040540 BasicObject 

A to, w dużym skrócie, to jak Ty snipe a nerd.

+2

To jest bardzo dobre wytłumaczenie. –

3

#<Class:Foo> to klasa własna/anonimowa danej klasy Foo. Jeśli ta eigen/klasa anonimowy również została rozszerzona, inna klasa eigen byłby został stworzony - który w ten sposób zostaje przedstawiony jako #<Class:#<Class:Foo>>

dominującą klasy EIGEN jest eigen klasa Object klasy, którego rodzic jest eigen klasa BasicObject. Podobnie rodzicem klasy własnej innej klasy własnej jest klasa własna klasy własnej klasy Object i tak dalej.

poniższy kod sprzężony with this explanation daje więcej spostrzeżeniami

p Foo.class 
p Foo.class.ancestors 
puts "-----------------" 
p Foo.singleton_class 
p Foo.singleton_class.ancestors 
puts "-----------------" 
p Foo.singleton_class.singleton_class 
p Foo.singleton_class.singleton_class.ancestors 

który wyprowadza

EIGEN klasowej hierarchii jak widać powyżej będzie powtarzać dowolną liczbę poziomów. Na przykład:

p Foo.singleton_class.singleton_class.singleton_class.singleton_class.singleton_class 
puts "-----------------" 
p Foo.singleton_class.singleton_class.singleton_class.singleton_class.singleton_class.ancestors 

Przede wyjść kodu

#<Class:#<Class:#<Class:#<Class:#<Class:Foo>>>>> 
----------------- 
[#<Class:#<Class:#<Class:#<Class:#<Class:Foo>>>>>, #<Class:#<Class:#<Class:#<Class:#<Class:Object>>>>>, #<Class:#<Class:#<Class:#<Class:#<Class:BasicObject>>>>>, #<Class:#<Class:#<Class:#<Class:Class>>>>, #<Class:#<Class:#<Class:#<Class:Module>>>>, #<Class:#<Class:#<Class:#<Class:Object>>>>, #<Class:#<Class:#<Class:#<Class:BasicObject>>>>, #<Class:#<Class:#<Class:Class>>>, #<Class:#<Class:#<Class:Module>>>, #<Class:#<Class:#<Class:Object>>>, #<Class:#<Class:#<Class:BasicObject>>>, #<Class:#<Class:Class>>, #<Class:#<Class:Module>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 
+0

Dobra robota. Miałem zamiar odpowiedzieć na to pytanie, ale chciałem uzyskać moją terminologię na samym początku, zanim zacząłem. Uderzyłeś mnie w to. Właśnie dlatego zadawałem pytania, które tutaj zadałem: http://stackoverflow.com/questions/31775576/nested-singleton-class-method-lookup#comment51574477_31775576 ​​ Udzielenie odpowiedzi na pytanie, gdzie metoda jest zdefiniowana, wymaga wyjaśnienia hierarchii dziedziczenia. – saghaulor

+0

@saghaulor Dzięki za upvote i miłe słowa –