【问题标题】:How do I get a list of all subclasses without instantiating first in Ruby如何在不首先在 Ruby 中实例化的情况下获取所有子类的列表
【发布时间】:2016-01-15 18:37:24
【问题描述】:

如果我有 4 个具有以下层次结构的类:

class MainClass < ActiveRecord::Base
  ...
end

class SubClassA < MainClass
  ...
end

class SubClassB < MainClass
  ...
end

class SubClassC < MainClass
  ...
end

如何在不遍历并创建其他每个类的实例的情况下获取 MainClass 的子类列表?

在新的 IRB 会话中,我可以进去说

irb(main)> MainClass.descendants
=> []

但是,如果我遍历并创建每个子类的实例,我会看到以下内容

irb(main)> SubClassA.new
=> #<SubClassA ...>
irb(main)> SubClassB.new
=> #<SubClassB ...>
irb(main)> SubClassC.new
=> #<SubClassC ...>
irb(main)> MainClass.descendants
=> [SubClassA(...), SubClassB(...), SubClassC(...)]

我基本上是在寻找一种以编程方式提供所有子类的方法,所以将来当我想添加 SubClassD、SubClassE 等时,我不必担心每个子类都会在用户之前在代码中实例化可以看到他们。

【问题讨论】:

  • 我在铁路控制台(rails 4.1.2)上试过。我得到了这个(而不是像你这样的空列表):-irb(main):012:0> MainClass.descendants => [SubClassA(表不存在),SubClassB(表不存在)]跨度>
  • 有趣。最初,我将我的子类拆分为它们自己的文件(sub_class_a.rb、sub_class_b.rb、sub_class_c.rb),所有这些文件都在同一级别,我得到了上面详述的内容。当我将所有类合并到同一个 main_class.rb 中时,descendants 方法就像你说的那样工作。

标签: ruby-on-rails ruby


【解决方案1】:

这是开发模式的产物,仅在首次引用时才加载类:解释器尚未读取这些文件 - 就 ruby​​ 而言,这些类实际上还不存在

解决方法是放

require_dependency "subclass_a"
require_dependency "subclass_b"
....

主类文件的底部(在类定义之外)

【讨论】:

  • 这个解决方案是特定于 RAILS 的吗?
  • 这是 - 行为不端的后代方法和 require_dependency 都是特定于 Rails 的。
  • 谢谢!这做到了。我什至没有想到这些文件没有在开发中加载
【解决方案2】:

有几种方法可以实现这一目标。通过使用诸如 descendants_tracker 之类的 gem:

class MainClass < ActiveRecord::Base
  extend DescendantsTracker
end

class SubClassA < MainClass 
end

...

MainClass.descendants # => [SubClassA]

或者你可以在你的父类中创建一个自定义的类,比如this

class  MainClass < ActiveRecord::Base
  def self.descendants
    ObjectSpace.each_object(Class).select { |klass| klass < self }
  end
end

class SubClassA < MainClass 
end

class SubClassB  < MainClass 
end

puts MainClass.descendants 

#=> SubClassA
    SubClassB  

您还可以检查依赖关系的级别。例如,如果您想要 MainClass 的直接子元素的数组,您可以使用 .subclasses 类方法

MainClass.subclasses  # => [SubClassA, SubClassB,...]

现在,如果您想要多个级别的子类深度,请使用.descendants。你可以找到一个例子here

【讨论】:

  • &lt; ActiveRecord::Base 可能与此答案无关。
  • 意味着不能在 ActiveRecord 模型上调用 .subclasses.descendants 方法?
  • 对,我正在尝试根据用户的问题调整我的答案。
  • 对不起,没有意识到这一点。那就没事了。
  • 别担心,这是一个真实的评论:)
【解决方案3】:

您可以使用ObjectSpace#each_object 来编译MainClass 的孩子的列表:

假设:

class MainClass            ; end
class SubClassA < MainClass; end
class SubClassB < MainClass; end
class SubClassC < SubClassB; end

MainClass 可能是子类这一事实无关紧要。

ObjectSpace.each_object(Class).select { |o| o < MainClass }.
  tap { |siblings| siblings.reject! { |klass| siblings.any? { |k| klass < k } } }
  #=> [SubClassB, SubClassA]  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 2014-06-18
    • 2022-12-03
    • 2023-01-12
    • 1970-01-01
    • 2020-12-19
    相关资源
    最近更新 更多