2017-08-25 115 views

回答

5

晶體中的元編程是使用macros實現的。

宏是在編譯時接收AST節點並生成粘貼到程序中的代碼的方法。

晶體已經實現了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" 

# ... 
+0

太好了swer!順便說一下,是否有可能看看這些方法的來源? (在這裏嘗試https://github.com/crystal-lang/crystal/blob/e2a1389e8165fb097c785524337d7bb7b550a086/src/compiler/crystal/macros.cr#L1508,但他們只是爲了文檔的目的) – opensas

+1

@opensas一些相關的實現是[這裏](https://github.com/crystal-lang/crystal/blob/master/src/compiler/crystal/macros/methods.cr#L1523-L1532)。但是,老實說,我不明白它是如何工作的。也許最好直接在GitHub(或Gitter)上提問... –