Testen Sie, ob eine Ruby-Klasse eine Unterklasse einer anderen Klasse ist

187

Ich möchte testen, ob eine Klasse von einer anderen Klasse erbt, aber dafür scheint es keine Methode zu geben.

class A
end

class B < A
end

B.is_a? A 
=> false

B.superclass == A
=> true

Eine triviale Implementierung dessen, was ich will, wäre:

class Class
  def is_subclass_of?(clazz)
    return true if superclass == clazz
    return false if self == Object
    superclass.is_subclass_of?(clazz)
  end
end

aber ich würde erwarten, dass dies bereits existiert.

Verwirrtheit
quelle
2
A.class #=> Class. Aus diesem Grund wird B.is_a? Afalse zurückgegeben.
Wen
was ist mitkind_of?
Akostadinov
1
kind_of?Testet, ob ein Objekt eine Instanz einer Klasse ist. Nicht, ob das Objekt von einer Klasse erbt.
Verwirrung
5
kind_of?ist ein Alias ​​vonis_a?
coreyward

Antworten:

355

Verwenden Sie einfach den <Operator

B < A # => true
A < A # => false

oder verwenden Sie den <=Operator

B <= A # => true
A <= A # => true
Marcel Jackwerth
quelle
13
@Brian Weil is_a?übersetzt in eine Instanz von . Bist keine Instanz von A, B.newis obwohl ( B.new.is_a? A # => true).
Marcel Jackwerth
4
Hmm, seltsame Syntax (wäre nicht meine erste Vermutung gewesen), aber danke für die Klarstellung!
Brian Armstrong
2
Dokumentation finden Sie unter Core API / Module / # < .
Webwurst
2
Meine Hassliebe zu Ruby geht weiter… Warum sollte ein Operator, der zum Deklarieren von Klassenbeziehungen verwendet wird, eine andere Funktion bereitstellen UND zwei verschiedene Möglichkeiten bereitstellen?
Ben Gotow
4
@BenGotow - 1. Da <kein Operator ist, handelt es sich um eine Methode. 2. Weil <nur nach einer Unterklasse sucht und <= auch self enthält.
Superleuchte
59

Auch verfügbar:

B.ancestors.include? A

Dies unterscheidet sich geringfügig von der (kürzeren) Antwort von, B < Aweil Benthalten ist in B.ancestors:

B.ancestors
#=> [B, A, Object, Kernel, BasicObject]

B < B
#=> false

B.ancestors.include? B
#=> true

Ob dies wünschenswert ist oder nicht, hängt von Ihrem Anwendungsfall ab.

Phrogz
quelle
24
Besser lesbar: B <= B(gleiches Ergebnis wie B.ancestors.include? B).
Marcel Jackwerth
Update: Die unmittelbar vorhergehende Lösung funktioniert mit 1.9+, während es keine "Vorfahren" gibt. in 1.9.
8
Dies wird Leute nicht verwirren, die mit der '<' - Syntax nicht vertraut sind, und aus diesem Grund bevorzuge ich sie.
Asfand Qazi
2
@SimonLepkin Wahrscheinlich nicht "eine Weile", es sei denn, es treten Mikrosekunden auf. ;) Ja, hinter den Kulissen durchlaufen die Methoden include?und die Ahnenkette . Dies geschieht in C, also sicherlich schneller als das Durchlaufen des Ruby-Arrays ... aber praktisch sollten die beiden nicht zu unterscheiden sein. <
Phrogz
1
@JunanChakma Basierend auf der Definition des englischen Wortes "Vorfahren" stimme ich zu, dass der Rückgabewert nicht enthalten sein sollte B. Aber es tut. In der Methodendokumentation heißt es: "Gibt eine Liste der in mod enthaltenen / vorangestellten Module zurück ( einschließlich mod selbst )." (Hervorhebung von mir). Ich vermute, es enthält eine eigene Klasse, um die Verwendung zu vereinfachen .include?, aber das ist nur Spekulation meinerseits.
Phrogz