Ruby 关于类名常量

zputee · 2013年04月29日 · 最后由 zputee 回复于 2013年04月30日 · 6060 次阅读

看了 RubyChina 关于常量的发言,引发了我对常量的兴趣:

  1. 类名为什么是常量?
  2. 类名常量的作用域?
  3. 类名常量和类的关系? 感谢@chenge @lgn21st @luikore 等等的鼓励和指教,感谢 RubyChina。

一、类名为什么是常量?

#当前类是Object
self.class
#=>Object
#添加一个类C
class C ; end
Object.constants.grep  /^C$/ 
#=>[:C]      
#这时在当前类Object中添加了常量 :C
#:所以类名是常量。

二、这里的类名常量 C,为什么是所有作用域可见?

类名常量 C 添加在 Object 类中 Object 是所有类的 superclass(BasicObject 除外),所以在所有类中都可见。

如果:

module M 
  class C; end
end
Object.constants.grep  /^M$/ 
#=>[:M]
Object.constants.grep  /^C$/ 
#=>[]
M.constants
#=>[:C]

#---------关于 Kernel#load 方法的实验----------
#person.rb
#class Person; end

class C
  class D
    def self.init
      load './person.rb'     
     end
     init              #load到Object,而不是C::D
  end
end
Object.constants.grep /^C$|^Person$/
#=>[:C,:Person]
Object.constants.grep /^D$/
#=>[]
C.constants
#=>[:D]

三、类名常量 C 和类 C 的关系

#类名常量C是指向(reference)类C的对象

class C
  def hi
    p 'hi, in class C'
  end
end
#变量cc
cc = C           
cc.name
#=>"C"
cc.new.hi
#=> 'hi, in class C'         

#改变常量C,(实验需要,平时不建议改变常量^_^)
C = "i'm string now"   #assign
#=>warning.....        
#C还是常量
Object.constants.grep  /^C$/
#=>[:C]  
C
#=>"i'm string now"
#指向了String类的对象。

#变量cc没变,指向类C
cc.name
#=>"C"
cc.new.hi        
#=> 'hi, in class C'

#D是常量
D = cc         
Object.constants.grep  /^C$|^D$/
#=>[:C,:D]
D.name
#=>"C"
#常量D指向类C

#打开类C,添加方法sing
class D       # D是指向类C的常量!!!
  def sing
    'singing'
  end
end
#常量D和变量cc共同指向C类
D.instance_methods(false)  
cc.instance_methods(false)
#=>[:hi,:sing] 

#通过变量cc,向类C 添加方法
cc.send(:define_method, :dance){"dance"}  
cc.instance_methods(false)
D.instance_methods(false)
#=>[:hi,:sing,:dance]

在 ruby 中,类也是一个普通的对象。

#1 楼 @baxiaopeng ruby 毕竟不是 JavaScript。类是对象。不是普通对象。‘ 对象中不能定义常量,对象不能 new,对象没有 Module 中 instance_methods

举个列子: class MyClass;end obj=MyClass.new 那么,obj 和 MyClass 都是对象,它们唯一的区别就是,obj 是变量,而 MyClass 是常量。 仅此而已。换句话说,就像类是对象一样,类名无非就是一个常量而已。 MyClass 是对象,所以它也有类,它的类就是 Class,而 Class 的 Class 就是它自己,Class 的 superclass 就是 Module,Module 的 superclass 就是 Object。Object 的 class 就是 Class。

#3 楼 @baxiaopeng 整个一个绕口令,郭德纲都说不了。

5 楼 已删除

#3 楼 @baxiaopeng @chenge 😄

class MyClass;end
obj=MyClass.new

MyClass.instance_methods.length
MyClass.methods.length
obj.methods.length

MyClass.methods
Module.instance_methods(false)
Class.instance_methods(false)

```

#4 楼 @chenge 哈哈,我刚写了个帖子,有个图,估计能看清楚一些。 http://www.cnblogs.com/baxiaopeng/archive/2013/04/29/3050497.html

类是对像,对象不是类。

class MyClass;end obj=MyClass.new 请来个 obj 中有 new 吗,能添加 instance_method,然后 new 一个对象吗? (obj.clone,obj.dup 不算,在 Javascript 中,基本可以说 对象是类,类是对象 )

@baxiaopeng 借花献佛 🌻

Class.superclass == Module; 
Module.superclass==Object; 
# (mixin Kernel)
Object.superclass == BasicObject;                      
BasicObject.superclass == nil
Kernel.class == Module
BasicObject.class = Class
Module.class == Class
Object.class == Class                     
Class.class == Class

以上错综复杂的关系,我给它起了个名字:Ruby 云! ,Ruby 云孕育了 Ruby 世界!

先有鸡还是先有蛋? 😄 Ask:Matz!

《元编程》有相似的图,最近有点心得,准备写一篇,到时请大家小拍。

@chenge

#9 楼 @zputee 也可以叫 Ruby 晕

#3 楼 @baxiaopeng

我认为下面这句话不准确:

换句话说,就像类是对象一样,类名无非就是一个常量而已。

我是这样理解的。(仅供参考)

类不是对象... 类是一个结构,类名才是对象,并且这个对象是一个常量。你可以通过这个对象 (或常量) 来引用 .

楼主的探索精神值得鼓励!!

不过在常量方面,似乎还有一些东西可以挖掘,给一个思考方向,直接在 Object 中定义一个常量 A, 与在 Kernel 中定义一个常量 A, 然后混入 Object, 在代码中,你都可以直接通过 A 的方式 (不加域限定符::) 来引用,为什么呢?

#8 楼 @zputee

你这话不准确. 能添加 instance_method,然后new一个对象吗?

当然可以。Class.new.new 不就是吗?我想你的意思是说,能 new 当前类的对象吗?

#14 楼 @zw963 Class.new.new 这个倒是比较新颖。

#14 楼 @zw963 @chenge

“我不想它被 assign , 或者 希望总可以被访问。”

你 Wave 命名的动机论很精彩。那个讨论,引发了我对常量的思考。 什么是动态语言的动态? Class.new.new.new 可以吗?

#12 楼 #13 楼 @zw963 代码在运行时都是内存中的结构

“有一些东西可以挖掘”

是的。最近学 ruby,因起点不高,写这贴子,费时不少。

class C; end    

类 C | 类名 :C | 类名常量 (类名是个常量)。 (类 类 类,有点累 ^_^)

常量的搜索:

  1. 当前作用域的常量 = self.class.constants + all superclass.constants + all mixinModule.contants
  2. 顺序:由近到远 C --> Object -->Kernel(Mixin)。

在 irb 中

TT = 1
self.class.constants.grep /^TT$/
#=>[:TT]
Object.constants.grep /^TT$/
#=>[:TT]

因为 TT 常量加入了 superclass --- Cbject 的常量中,因此所有作用域可见


原贴中代码在 irb 或 pry 中走一遍,欢迎小拍。应该有些需要改进的地方。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号