为什么实例变量在块内部看似消失?

Dan*_*Tao 26 ruby block instance-variables savon

伙计们,请原谅我.对于Ruby来说,我充其量只是一个新手.我只是想知道对我来说看起来很奇怪的行为的解释.

我正在使用Savon库与我的Ruby应用程序中的SOAP服务进行交互.我注意到以下代码(在我编写的用于处理此交互的类中)似乎传递空值,我希望成员字段的值可以去:

create_session_response = client.request "createSession" do
  soap.body = {
    :user => @user, # This ends up being empty in the SOAP request,
    :pass => @pass  # as does this.
  }
end
Run Code Online (Sandbox Code Playgroud)

尽管这是一个事实,即@user@pass已初始化为非空字符串.

当我更改代码以使用本地代码时,它的工作方式与我期望的一样:

user = @user
pass = @pass

create_session_response = client.request "createSession" do
  soap.body = {
    :user => user, # Now this has the value I expect in the SOAP request,
    :pass => pass  # and this does too.
  }
end
Run Code Online (Sandbox Code Playgroud)

我猜这个奇怪的(对我来说)行为必须与我在一个街区内的事实有关; 但实际上,我不知道.有人可以在这个上启发我吗?

Phr*_*ogz 36

首先,@user不是Ruby中的"私有变量"; 它是一个实例变量.实例变量在当前对象的范围内(self引用的内容)可用.我已编辑了您问题的标题,以便更准确地反映您的问题.

块就像一个函数,一组代码将在以后执行.通常,该块将在定义块的范围内执行,但也可以在另一个上下文中评估块:

class Foo
  def initialize( bar )
    # Save the value as an instance variable
    @bar = bar
  end
  def unchanged1
    yield if block_given? # call the block with its original scope
  end
  def unchanged2( &block )
    block.call            # another way to do it
  end
  def changeself( &block )
    # run the block in the scope of self
    self.instance_eval &block
  end
end

@bar = 17
f = Foo.new( 42 )
f.unchanged1{ p @bar } #=> 17
f.unchanged2{ p @bar } #=> 17
f.changeself{ p @bar } #=> 42
Run Code Online (Sandbox Code Playgroud)

因此,您要么在@user设置范围之外定义块,要么实现client.request导致稍后在另一个范围内评估块.你可以写一下:

client.request("createSession"){ p [self.class,self] }
Run Code Online (Sandbox Code Playgroud)

了解self块中当前对象的类型.

它们在你的情况下"消失"而不是抛出错误的原因是Ruby允许你请求任何实例变量的值,即使从未为当前对象设置了值.如果从未设置过该变量,那么您只需返回nil(如果启用了它们,则会发出警告):

$ ruby -e "p @foo"
nil

$ ruby -we "p @foo"
-e:1: warning: instance variable @foo not initialized
nil
Run Code Online (Sandbox Code Playgroud)

正如您所发现的,块也是封闭的.这意味着当它们运行时,它们可以访问在定义块的同一范围内定义的局部变量.这就是您的第二组代码按预期工作的原因.闭包是一种很好的方法,可以锁定一个值,以便以后使用,例如在回调中.

继续上面的代码示例,您可以看到局部变量可用,无论评估块的范围如何,并且优先于该范围内的同名方法(除非您提供显式接收器):

class Foo
  def x
    123
  end
end
x = 99 
f.changeself{ p x } #=> 99
f.unchanged1{ p x } #=> 99
f.changeself{ p self.x } #=> 123
f.unchanged1{ p self.x } #=> Error: undefined method `x' for main:Object
Run Code Online (Sandbox Code Playgroud)


And*_*ggs 5

文档:

Savon :: Client.new接受一个块,您可以在其中访问本地变量甚至是您自己的类中的公共方法,但实例变量不起作用.如果您想知道为什么会这样,我建议您阅读有关委托的instance_eval.

当问到这个问题时,可能没有详细记录.