Ruby 方法instance_eval() 和send() 是否否定了私有可见性的好处?

发布于 2024-07-20 21:01:19 字数 569 浏览 8 评论 0原文

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
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

Looking at the example above which relates to the Widget class (below), the send and instance_eval methods violate all of the protections provided by private and protected visibility. If so, why bother with private and protected access in Ruby at all since there is no guarantee that your definitions will be honored?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

长发绾君心 2024-07-27 21:01:19

ruby 相信赋予您做您想做的事情的能力。 它只是不容易不经意地开枪——如果你想破坏私有声明,你必须使用明确你正在这样做的语法。 请注意,最终决定代码应该或不应该做什么的人是使用库的人,而不是编写库的人。

ruby believes in giving you the power to do what you want. it just doesn't make it easy to inadvertently shoot your foot off - if you want to subvert the private declarations, you have to use syntax that makes it clear you are doing so. note that the person finally deciding what the code should or shouldn't do is the person using a library, not the person writing it.

旧人 2024-07-27 21:01:19

我无法发表评论,因为代表较低:/。

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

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

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

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

I cannot comment, because of low rep :/.

Redefining send is no use, because send is just the common name for __send__ (thats underscore,underscore,"send",underscore,underscore), which is the method actually implementing message sending. Redefining any __method__ is not recommended. Additionally, the other person can also reopen the class and revert the definition:

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

In Ruby 1.9, the behaviour is slightly different: #send actually honors visibility, __send__ doesn't.

private in Ruby has more of a declarative purpose: methods declared as private are an implementation detail and not an API detail. You are not allowed to send a message from the outside by accident. But anyone can still forcefully circumvent that restriction, if they see fit - on their own account.

薄荷→糖丶微凉 2024-07-27 21:01:19

如果您确实想要保护 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

If you really want to protect instances of Widget, you can do this (and a bunch of other stuff; the code here is not a complete security solution, merely indicative):

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
帅的被狗咬 2024-07-27 21:01:19

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

At the very least you express what the public API of the Widget class is.

好多鱼好多余 2024-07-27 21:01:19

带回家的信息是:别打扰。

Ruby 和 Python 一样,在沙箱方面绝对很糟糕。 如果你试图锁定某些东西,很可能总会有某种方法来绕过它。 在 Ruby 中获取私有属性的多种方法证明了我的观点。

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

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

The take home message is: don't bother.

Ruby, like Python, absolutely sucks at sandboxing. If you try to lock something down, chances are there will always be some way to get around it. The multitude of ways to get a private attribute in Ruby proves my point.

Why? Because they are designed to be that way. Both languages are designed so that they can be poked around with at runtime – it's what gives them their power. By sealing up your class, you're depriving others of the power that Ruby's metaprogramming provides.

Java has reflection. C++ has pointers. Even Haskell has unsafePerformIO. If you want to protect your program, you will need to protect it on the operating system level, not using the language.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文