Ruby:为什么传递给send()的输入被包装在Hash中?

我在 Ruby 中使用 send() 方法来调用我定义的方法。该方法采用OpenStruct.

这是一个片段,显示了我如何使用send以下方法调用我的方法:

my_open_struct = OpenStruct.new(foo: "foo")

result = @my_object.send(
            :my_method_that_takes_an_openstruct,
            name_of_openstruct_param: my_open_struct)

的问题是,内部my_method_that_takes_an_openstructOpenStruct参数是得到包裹在一个Hash,从而导致在记录这样的输出:

Just before calling send #<OpenStruct foo="foo">
Inside my_method_that_takes_an_openstruct: {:name_of_openstruct_param=>#<OpenStruct foo="foo">}

为什么会发生这种情况,我该如何防止这种包装行为?

回答

我假设my_method看起来像这样

class Foo
  def my_method(arg)
    puts "#{arg}"
  end
end

如果你从一个Python背景的,你可能会认为我们可以称之为my_method是两种foo.my_method(1)foo.my_method(arg: 1)。但这不是它在 Ruby 中的工作方式。在 Ruby 中,参数要么是命名的,要么是定位的。为了命名一个参数,我们在它后面加上一个冒号。

class Foo
  def my_method(arg:)
    puts "#{arg}"
  end
end

现在,我们可以做的Foo.new.my_method(arg: 1)Foo.new.send(:my_method, arg: 1),但它是不正确Foo.new.my_method(1)

您获得散列的原因是兼容性技巧。在旧版本的 Ruby 中,在我们命名参数之前,约定是在末尾采用单个哈希参数

def foo(a, b, opts)
  ...
end

然后以下两个调用将是等效的(前者是后者的语法糖)

foo(1, 2, foo: "bar", bar: "baz")
foo(1, 2, {foo: "bar", bar: "baz"})

基本上,如果任何命名参数出现在参数列表中,它们将被转换为单个散列并作为最终参数传递给函数。

此行为自 Ruby 2.7 起已弃用,并在 Ruby 3.0 中删除。现在正确的约定是采用显式命名参数,并且最新版本的 Ruby 支持双 splat**运算符,用于将散列转换为命名参数,类似于具有相同名称的 Python 运算符。


以上是Ruby:为什么传递给send()的输入被包装在Hash中?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>