有没有办法从Ruby中的实例调用私有Class方法?

问题描述 投票:15回答:7

当然,除了self.class.send :method, args...。我想在类和实例级别上创建一个相当复杂的方法,而不需要复制代码。


更新:

@Jonathan Branam:这是我的假设,但我想确保没有其他人找到方法。 Ruby中的可见性与Java中的可见性非常不同。你也很正确private不能在类方法上工作,尽管这会声明一个私有类方法:

class Foo
  class <<self
    private
    def bar
      puts 'bar'
    end
  end
end

Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
ruby scope visibility class-method access-specifier
7个回答
12
投票

这是一个与问题一起提供的代码片段。在类定义中使用“private”不适用于类方法。您需要使用“private_class_method”,如以下示例所示。

class Foo
  def self.private_bar
    # Complex logic goes here
    puts "hi"
  end
  private_class_method :private_bar
  class <<self
    private
    def another_private_bar
      puts "bar"
    end
  end
  public
  def instance_bar
    self.class.private_bar
  end
  def instance_bar2
    self.class.another_private_bar
  end
end

f=Foo.new
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class

我没有办法解决这个问题。文档说您无法指定私有方法的接收。此外,您只能从同一实例访问私有方法。类Foo是与给定Foo实例不同的对象。

不要把我的答案作为最终答案。我当然不是专家,但我想提供一个代码片段,以便其他尝试回答的人将拥有正确的私有类方法。


3
投票

让我为这个或多或少奇怪的解决方案和非解决方案列表做出贡献:

puts RUBY_VERSION # => 2.1.2

class C
  class << self
    private def foo
      'Je suis foo'
    end
  end

  private define_method :foo, &method(:foo)

  def bar
    foo
  end
end

puts C.new.bar # => Je suis foo
puts C.new.foo # => NoMethodError

0
投票

如果你的方法只是一个utility function(也就是说,它不依赖于任何实例变量),你可以将该方法放入一个模块中,并将includeextend放在该类中,以便它既可以作为私有类方法又可以作为私有实例使用方法。


0
投票

现在你不再需要辅助方法了。您可以使用方法定义简单地内联它们。这对Java人员来说应该非常熟悉:

class MyClass

  private_class_method def self.my_private_method
    puts "private class method"
  end

  private def my_private_method
    puts "private instance method"
  end

end

不,你不能从实例方法调用私有类方法。但是,您可以使用private_constant帮助器方法在私有嵌套类中将私有类方法实现为公共类方法。有关详细信息,请参阅this blogpost


0
投票

这是使用“真正的”私有类方法的方法。

class Foo
  def self.private_bar
    # Complex logic goes here
    puts "hi"
  end
  private_class_method :private_bar
  class <<self
    private
    def another_private_bar
      puts "bar"
    end
  end
  public
  def instance_bar
    self.class.private_bar
  end
  def instance_bar2
    self.class.another_private_bar
  end
  def calling_private_method
    Foo.send :another_private_bar
    self.class.send :private_bar
  end
end
f=Foo.new
f.send :calling_private_method 
 # "bar"
 # "hi"
Foo.send :another_private_bar
# "bar"

干杯


0
投票

这可能是最“本土的香草Ruby”方式:

class Foo
  module PrivateStatic # like Java
    private def foo
      'foo'
    end
  end
  extend PrivateStatic
  include PrivateStatic

  def self.static_public_call
    "static public #{foo}"
  end

  def public_call
    "instance public #{foo}"
  end
end

Foo.static_public_call # 'static public foo'
Foo.new.public_call # 'instance public foo'
Foo.foo # NoMethodError: private method `foo' called for Foo:Class
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x00007fa154d13f10>

使用一些Ruby元编程,你甚至可以看起来像:

class Foo
  def self.foo
    'foo'
  end

  extend PrivateStatic
  private_static :foo
end

Ruby的元编程非常强大,因此您可以在技术上实现您可能想要的任何范围规则。话虽这么说,我仍然更喜欢第一个变体的清晰度和minimal surprise


-1
投票

除非我误解,否则你不需要这样的事情:

class Foo
    private
    def Foo.bar
        # Complex logic goes here
        puts "hi"
    end

    public
    def bar
        Foo.bar
    end
end

当然,如果你想避免硬编码类名,你可以改变第二个定义来使用你的self.class.send方法......

© www.soinside.com 2019 - 2024. All rights reserved.