获取Crystal中继承链信息

4

出于好奇并为了了解Crystal的一般结构,我正在寻找一些反射功能,以便更好地理解继承链是如何建立的。

我在想像ruby的 superclass, ancestors 或者 included_modules 方法那样的东西。

Crystal语言中是否有类似的功能?

此外,拥有某种能够展示整体情况的图表将非常有用。

1个回答

8

在Crystal中,元编程是通过实现的。

宏是一种方法,它在编译时接收AST节点并生成代码,然后将其粘贴到程序中。

Crystal已经实现了superclassancestors的实现,可以在编译时返回结果。所以你可以这样做:

{{ pp MyClass.superclass }}
{{ pp MyClass.ancestors }}

为了方便,您可以编写自己的宏来检查继承。考虑到学习目的,它可能看起来像这样:

class Class
  def superclass
    {{ @type.superclass }}
  end

  def ancestors
    {% if @type.ancestors.size > 0 %}
      {{ @type.ancestors }}
    {% else %}
      [] of Nil
    {% end %}
  end

  def included_modules
    {% if @type.ancestors.any? { |a| a.class.stringify.ends_with?(":Module") } %}
      {{ @type.ancestors.select { |a| a.class.stringify.ends_with?(":Module") } }}
    {% else %}
      [] of Nil
    {% end %}
  end

  def inheritance_chain
    String.build do |chain|
      cls = self
      chain << cls
      while !(cls = cls.superclass).nil?
        chain << " > #{cls}"
      end
    end
  end
end

然后您可以进行检查:
class A
end

module B
end

require "crystal/system/random"

class C < A
  include B
  include Crystal::System::Random
end

C.name             # => "C"
C.superclass       # => A
C.ancestors        # => [Crystal::System::Random, B, A, Reference, Object]
C.included_modules # => [Crystal::System::Random, B]
A.included_modules # => []

如果您继续深入了解:

C.superclass                                                    # => A
C.superclass.try &.superclass                                   # => Reference
C.superclass.try &.superclass.try &.superclass                  # => Object
C.superclass.try &.superclass.try &.superclass.try &.superclass # => nil

现在使用 inheritance_chain

Int32.inheritance_chain                          # => "Int32 > Int > Number > Value > Object"
String.inheritance_chain                         # => "String > Reference > Object"
Float64.inheritance_chain                        # => "Float64 > Float > Number > Value > Object"
Array(Bool).inheritance_chain                    # => "Array(Bool) > Reference > Object"
Hash(Bool, Bool).inheritance_chain               # => "Hash(Bool, Bool) > Reference > Object"
Tuple(Char).inheritance_chain                    # => "Tuple(Char) > Struct > Value > Object"
NamedTuple(s: String, b: Bool).inheritance_chain # => "NamedTuple(s: String, b: Bool) > Struct > Value > Object"
Nil.inheritance_chain                            # => "Nil > Value > Object"
Regex.inheritance_chain                          # => "Regex > Reference > Object"
Symbol.inheritance_chain                         # => "Symbol > Value > Object"
Proc(Int32).inheritance_chain                    # => "Proc(Int32) > Struct > Value > Object"
Set(String).inheritance_chain                    # => "Set(String) > Struct > Value > Object"
Exception.inheritance_chain                      # => "Exception > Reference > Object"
Class.inheritance_chain                          # => "Class > Value > Object"

# union
alias UnionType = Int32 | Nil | String
UnionType.inheritance_chain                      # => "(Int32 | String | Nil) > Value > Object"

# nilable
Int32?.inheritance_chain                         # => "(Int32 | Nil) > Value > Object"

# pointer
alias Int32Ptr = Int32*
Int32Ptr.inheritance_chain                       # => "Pointer(Int32) > Struct > Value > Object"

# ...

很棒的答案!顺便问一下,能否看一下这些方法的源代码?(在这里尝试过https://github.com/crystal-lang/crystal/blob/e2a1389e8165fb097c785524337d7bb7b550a086/src/compiler/crystal/macros.cr#L1508,但它们只是用于文档目的) - opensas
使用 {{ pp MyClass.superclass }} 替代 pp({{ MyClass.superclass }}),以避免在 Crystal 0.34+ 中出现 "Invalid memory access (signal 11)"。 - UrsaDK
@Vitali 确实是个好答案!如果可以的话,您能否再添加一些关于如何获取继承链中的方法的信息呢?(我特别困惑于如何以相反的顺序获取它们,即先获取最派生的类)。 - Sunder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接