Ruby 方法 instance_eval() 和 send() 不会否定私有可见性的好处吗?

Posted

技术标签:

【中文标题】Ruby 方法 instance_eval() 和 send() 不会否定私有可见性的好处吗?【英文标题】:Don't the Ruby methods instance_eval() and send() negate the benefits of private visibility? 【发布时间】:2010-10-28 03:38:18 【问题描述】:
w = Widget.new # Create a Widget
w.send :utility_method # Invoke private method!
w.instance_eval  utility_method  # Another way to invoke it
w.instance_eval  @x  # Read instance variable of w

查看上面与 Widget 类相关的示例(如下),send 和 instance_eval 方法违反了私有和受保护可见性提供的所有保护。如果是这样,为什么还要在 Ruby 中使用私有和受保护的访问,因为无法保证您的定义会得到尊重?

class Widget
  def x # Accessor method for @x
   @x
  end
  protected :x # Make it protected
  def utility_method # Define a method
   nil
  end
  private :utility_method # And make it private
end

【问题讨论】:

类似问题:***.com/questions/2519136/… 【参考方案1】:

ruby 相信赋予你做你想做的事的能力。 不经意间让你的脚离开并不容易 - 如果你想颠覆私有声明,你必须使用清楚地表明你正在这样做的语法。请注意,最终决定代码应该做什么或不应该做什么的人是使用库的人,而不是编写它的人。

【讨论】:

【参考方案2】:

我不能评论,因为低代表:/。

重新定义send是没有用的,因为send只是__send__的通用名称(即下划线,下划线,“send”,下划线,下划线),也就是真正实现消息发送的方法。不建议重新定义任何 __method__。此外,其他人也可以重新打开课程并恢复定义:

class Widget
  def send(method, *args, &block)
    super
  end
  #and so on
end

在 Ruby 1.9 中,行为略有不同:#send 实际上尊重可见性,而 __send__ 没有。

Ruby 中的private 具有更多的声明性目的:声明为私有的方法是实现细节而不是API 细节。不允许您意外地从外部发送消息。但是,如果他们认为合适的话,任何人仍然可以强行规避这一限制——以他们自己的名义。

【讨论】:

【参考方案3】:

如果您真的想要保护Widget 的实例,您可以这样做(以及其他一些东西;这里的代码不是完整的安全解决方案, 只是指示性的):

class Widget

  def some_public_method
    ...
  end

  private

  def utility_method
    ...
  end

  def send(method, *args, &block)
    raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
  end

  def instance_eval(&block)
    raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
  end

  class <<self
    private
    def class_eval(&block)
      raise NotImplementedError.new('Widget is secure. Stop trying to hack me.')
    end
  end
end

Widget.freeze

【讨论】:

(但我不推荐它,因为 Widget 几乎与所有使用元编程的库都不兼容。) 有趣的潜在解决方案。你通过重新定义class_eval来防止什么?我不明白最后一节:“class class class 方法 class_eval,而不是实例方法。见***.com/questions/678037。我保护它的原因是你不能修改类来取消它的安全。您可能还应该冻结课程。 别忘了发送。即便如此,这也可以通过在 require 'widget' 之前覆盖 Module#method_added 来预先破解。如果不冻结,覆盖 class_eval 不会给你带来太多好处,b/c 然后你仍然可以使用class Widget ... end 语法。简而言之,人们可以修改你的代码。学会接受它,它是一种解释性语言。【参考方案4】:

至少你表达了 Widget 类的公共 API 是什么。

【讨论】:

【参考方案5】:

带回家的信息是:不要打扰。

Ruby 和 Python 一样,在沙盒方面绝对烂透了。如果您尝试锁定某些东西,那么总会有一些方法可以绕过它。在 Ruby 中获取私有属性的多种方法证明了我的观点。

为什么?因为它们被设计成那样。这两种语言的设计都是为了在运行时可以随意使用它们——这就是赋予它们强大功能的原因。通过封闭你的类,你剥夺了其他人使用 Ruby 元编程提供的能力。

Java 有反射。 C++ 有指针。甚至 Haskell 也有 unsafePerformIO。如果你想保护你的程序,你需要在操作系统级别保护它,而不是使用语言。

【讨论】:

以上是关于Ruby 方法 instance_eval() 和 send() 不会否定私有可见性的好处吗?的主要内容,如果未能解决你的问题,请参考以下文章

201707《Ruby元编程》

「译」懂点那啥编译

删除/取消定义类方法

执行一个对象的实例方法,并在其块中调用另一个对象的实例方法

ruby 扩展了类和实例方法的Ruby模块

Ruby 方法的测量和基准时间