Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 为什么实例变量在块中似乎消失了?_Ruby_Block_Instance Variables_Savon - Fatal编程技术网

Ruby 为什么实例变量在块中似乎消失了?

Ruby 为什么实例变量在块中似乎消失了?,ruby,block,instance-variables,savon,Ruby,Block,Instance Variables,Savon,原谅我,伙计们。说到Ruby,我充其量只是个新手。我只是想知道对我来说很奇怪的行为的解释 我正在使用该库与Ruby应用程序中的SOAP服务进行交互。我注意到以下代码(在我为处理此交互而编写的类中)似乎传递了空值,我希望成员字段的值会传递到其中: create_session_response = client.request "createSession" do soap.body = { :user => @user, # This ends up being empty i

原谅我,伙计们。说到Ruby,我充其量只是个新手。我只是想知道对我来说很奇怪的行为的解释

我正在使用该库与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
尽管
@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

我猜(对我来说)这种奇怪的行为一定与我在一个街区内的事实有关;但真的,我不知道。有人能告诉我这一点吗?

在第一种情况下,
self
计算为
client.request('createSession')
,它没有这些实例变量


在第二种情况下,变量作为闭包的一部分引入到块中。

解决此问题的另一种方法是将对对象的引用带入块中,而不是多次枚举每个所需的属性:

o = self
create_session_response = client.request "createSession" do
  soap.body = {
    :user => o.user,
    :pass => o.pass
  }
end

但是现在您需要属性访问器。

首先,
@user
在Ruby中不是“私有变量”;这是一个很好的例子。实例变量在当前对象的范围内可用(self所指的
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
因此,要么您在设置
@user
的作用域之外定义块,要么
client.request的实现导致稍后在另一个作用域中计算块。您可以通过以下方式找到答案:

client.request("createSession"){ p [self.class,self] }
了解块中当前的
self
是什么类型的对象

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

$ruby-e“p@foo”
无
$ruby-我们“p@foo”
-e:1:警告:实例变量@foo未初始化
无
正如您所发现的,块也是。这意味着,当它们运行时,它们可以访问在定义块的相同范围内定义的局部变量。这就是为什么您的第二组代码按预期工作的原因。闭包是锁定值以供以后使用的一种很好的方法,例如在回调中

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

发件人:

Savon::Client.new接受一个块,在该块中您可以从自己的类访问局部变量甚至公共方法,但实例变量不起作用。如果你想知道为什么会这样,我建议你读一下关于委托的实例评估


当问到这个问题时,可能没有很好的记录。

谢谢!我很难用自己的代码块复制它;看到您的示例使用
instance\u eval
帮助我澄清了问题。@Dan不客气。我在示例的末尾添加了更多内容,显示了在简单情况下闭包的作用?否则,我的N.times{}块无法访问实例变量并使用o=self解决问题,现在就变成了一个语法混乱:(@Konrads是什么的一个很好的解决方案?用你的代码、需求和愿望问一个新问题。对其他人的问题的答案随机添加一段代码注释不会给你带来好的帮助。
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