zoukankan      html  css  js  c++  java
  • 一步一步学Ruby(十七):Ruby动态特性

    Ruby中的一切都是动态的,例如,我们可以在程序运行时,动态的添加方法,类等。前面我们已经看到了Ruby的动态特性,例如:给单个对象添加方法,重新打开类等。

    如果熟悉Rails,就知道ActiveRecord提供基于数据库表的字段名的方法。每一个字段都有一个方法,这个就依赖于Ruby的动态特性。

    一、单例类的位置

    我们可以为一个对象定义只属于自己的方法
    obj=Object.new
    def obj.say
        puts "Hello World"
    end
    obj.say  #输出 Hello World
    那么单例方法定义在哪里呢?Ruby把单例方法定义在单例类(singleton class)中.每一个对象实际上都有两个类:
    • 多个实例共享的类
    • 单例类

    对象调用的方法就是定义在这两个类中的方法,以及祖先类和混含模块中的方法,对象可以调用它所属的类的实例方法,也可以调用单例类中的方法。单例类是匿名的,他们是类对象的实例(Class类的实例),但他们是自动生成且没有名字

    str="Hello World"
    class<<str
       def bye
          self+", bye"
       end
    end
    puts str.bye

    输出 "Hello World, bye"

    在方法查找上,单例方法是最先找到的。

    二、eval方法

    这个和其它很多语言一样,具有在运行时执行以字符串形式保存代码的的功能。

    1.直接执行代码

    image

    2. 运行时提供方法名的例子
    print "Greeting Method:"
    m=gets.chomp
    eval("def #{m};puts 'Hello'; end")
    eval(m)
    输出:Hello
    如果输入hi, eval求职的字符串是 def hi; puts 'Hello'; end
    三、eval的危险性
    eval很强大,但是它也潜在着危险,这个有点像sql注入
    假如,上面我们输入的不是hi而是下面的内容
    hi; end; system("rm -rf /*"); #

    eval求值以后是这样的

    def hi; end; system("rm -rf /*"); # puts  'Hello'; end
    求值的结果是:#后面的所有内容会被作为注释忽略掉。使用system命令试图删除系统所有文件
    Ruby有一个全局变量$SAFE(取值范围是0到4)、以获取对如非法写文件这一类危险的防护。
    四、instance_eval
    该方法把self变为instance_eval调用的接收者,对字符串或代码块进行求职
    p self
    a=[]
    a.instance_eval(p self)
    输出:
    main
    []
    instance_eval常用于访问其它对象的私有数据,特别是实例变量
    class C
      def initialize
        @a=1
      end
    end
    c=C.new
    c.instance_eval {puts @a}
     
    五、class_eval
    class_eval可进入类定义体中
    c=Class.new
    c.class_eval do
      def some_method
        puts "created in class_eval"
      end
    end
    
    c=c.new
    c.some_method
    利用class_eval可以访问外围作用域的变量。
    var="test variable"
    class C
      puts var
    end
    C.class_eval {puts var}
    变量var在标准的类定义体的作用域之外,但是在class_eval的代码块的作用域之内
    image
    当在class_eval的块中定义一个实例方法时,又有不同
    var="test"
    class C
    end
    C.class_eval {def hi; puts var; end}
    c.new.hi
    # undefined local variable or method `c' for main:Object (NameError)
    但我们可以使用另外一种方法
    C.class_eval {define_method("hi"){ puts var}}
    六、Proc对象
    pr=Proc.new {puts "Hello from inside of proc block"}
    pr.call
    #输出: Hello from inside of proc block
    1、做为闭包的Proc对象
    Proc对象随身携带了它的上下文,下面上下文包含一个变量a,该变量被赋值为一个特殊的字符串保存在Proc对象中。像这样带着产生它的上下文信息的
    一段代码被称为闭包。产生一个闭包就像是打包一件行李:任何时候打开行李,都包含打包进去的东西,在打开一个闭包时(通过调用它),它包含产生
    它的时候你放进去的东西。
    def call_proc(pr)
      a="Jack"
      puts a
      pr.call
    end
    a="Tom"
    pr=Proc.new {puts a}
    pr.call
    call_proc(pr)
    #输出: Tom
    #      Jack
    #      Tom
    2. Proc对象的参数
    产生Proc对象时所提供的代码块可以接收参数
    pr=Proc.new {|x| puts "#{x} is better man"}
    pr.call("Jack")
    输出:Jack is better man
    七、匿名函数(lambda)
    lam=lambda {puts "Hello World"}
    lam.call
    输出: Hello World
    lambda不是Lambda类的对象,他们是Proc类的对象
    lam.class
    输出: Proc

    和所有的Proc对象一样,lambda是闭包;他们随身携带了生成他们的局部的上下文环境

    lambda生成的Proc对象和用Proc.new生成的对象之间的差别与return有关,lambda中的return从lambda返回,而Proc中的return从外围方法返回

    def test_return
      l=lambda {return}
      l.call
      puts "I am here"
      p=Proc.new {return}
      p.call
      puts "bye"
    end
    test_return
    输出: "I am here"
    八、再论代码块
    可以在方法中吧代码块转换成Proc对象,可以通过参数中最后一个变量,且必须&开头来捕获代码块
    def say_block(&block)
      block.call
    end
    say_block {puts "How are you?"}
    #output: how are you?
    def test_block(x,y,  &block)
      puts x+y
      block.call
    end
    test_block(1,2) {puts "Hi"}
    #output: 3 
    #        Hi
    
    也可以将Proc对象或lambda转换为代码块
    def say_block(&block)
      block.call
    end
    
    lam=lambda {puts "Hello world"}
    say_block(&lam)
    #output: Hello world
    
    
    本文作者:王德水
    未经同意,禁止转载
  • 相关阅读:
    python:封装连接数据库方法
    Python:self理解
    python:pytest优秀博客
    python:pytest中的setup和teardown
    python:单元测试框架pytest的一个简单例子
    java中的static关键字解析
    浅谈Java中的final关键字
    SpringMVC+Spring+Mybatis框架集成
    Mybatis学习总结(四)——输入映射和输出映射
    Mybatis学习总结(三)——SqlMapConfig.xml全局配置文件解析
  • 原文地址:https://www.cnblogs.com/cnblogsfans/p/1391004.html
Copyright © 2011-2022 走看看