在 Ruby 中初始化类实例变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4901208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:19:46  来源:igfitidea点击:

Initializing class instance variables in Ruby

ruby-on-railsrubyoop

提问by HalloDu

I am working on a small rails app and have a problem with ruby's OOP model. I have the following simplified class structure.

我正在开发一个小型 Rails 应用程序,但 ruby​​ 的 OOP 模型有问题。我有以下简化的类结构。

class Foo
  protected
    @bar = []
    def self.add_bar(val)
      @bar += val
    end
    def self.get_bar
      @bar
    end
end

class Baz < Foo
  add_bar ["a", "b", "c"]
end

My problem is now, that when I call add_bar in the class definition of Baz, @baris apparently not initialized and I get an error that the +Operator is not available for nil. Calling add_baron Foodirectly does not yield this problem. Why is that and how can I initialize @barcorrectly?

我现在的问题是,当我在 Baz 的类定义中调用 add_bar 时,@bar显然没有初始化,并且我收到一个错误,即+Operator 不可用于nil. 调用add_barFoo直接不会产生这个问题。为什么会这样,我该如何@bar正确初始化?

To make clear what I want, I will point out the behavior I would expect from these classes.

为了明确我想要什么,我将指出我期望从这些类中得到的行为。

Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar # => ["a", "b"]
Baz.get_bar # => ["a", "b", "1", "2"]

How could I achieve this?

我怎么能做到这一点?

回答by r00k

Short answer:instance variables don't get inherited by subclasses

简短回答:实例变量不会被子类继承

Longer answer:the problem is that you wrote @bar = []in the body of the class (outside any method). When you set an instance variable, it is stored on whatever is currently self. When you're in a class body, selfis the class object Foo. So, in your example, @foogets defined on the class object Foo.

更长的答案:问题是你写@bar = []在类的主体中(在任何方法之外)。当您设置一个实例变量时,它会存储在当前的任何内容上self。当你在一个类体中时,self是类对象 Foo。因此,在您的示例中,@foo在类对象 Foo 上定义。

Later, when you try to look up an instance variable, Ruby looks in whatever is currently self. When you call add_bar from Baz, selfis Baz. Also selfis STILL Baz in the body of add_bar (even though that method is in Foo). So, Ruby looks for @barin Baz and can't find it (because you defined it in Foo).

稍后,当您尝试查找实例变量时,Ruby 会查找当前self. 当你从 Baz 调用 add_bar 时,self就是 Baz。也self仍然是在巴兹add_bar的主体(即使该方法是在富)。所以,Ruby@bar在 Baz 中寻找却找不到(因为你在 Foo 中定义了它)。

Here's an example that might make this clearer

这是一个可能使这一点更清楚的例子

class Foo
  @bar = "I'm defined on the class object Foo. self is #{self}"

 def self.get_bar
    puts "In the class method. self is #{self}"    
    @bar
  end

  def get_bar
    puts "In the instance method. self is #{self} (can't see @bar!)"
    @bar
  end
end

>> Foo.get_bar
In the class method. self is Foo
=> "I'm defined on the class object Foo. self is Foo"

>> Foo.new.get_bar
In the instance method. self is #<Foo:0x1056eaea0> (can't see @bar!)
=> nil

This is admittedly a bit confusing, and a common stumbling point for people new to Ruby, so don't feel bad. This concept finally clicked for me when I read the 'Metaprogramming' chapter in Programming Ruby(aka "The Pickaxe").

诚然,这有点令人困惑,并且是 Ruby 新手的常见绊脚石,所以不要感到难过。当我阅读Ruby 编程(又名“镐”)中的“元编程”一章时,这个概念终于对我产生了影响。

How I'd solve your problem:Look at Rails' class_attributemethod. It allows for the sort of thing you're trying to do (defining an attribute on a parent class that can get inherited (and overidden) in its subclasses).

我将如何解决您的问题:查看 Rails 的class_attribute方法。它允许您尝试做的事情(在父类上定义可以在其子类中继承(和覆盖)的属性)。

回答by khelll

Well, since @bar is defined as class instance variable then it's limited to the class Foo. Check this:

好吧,因为@bar 被定义为类实例变量,所以它仅限于类Foo。检查这个:

class Foo
    @bar = []
end

class Baz < Foo
end

Foo.instance_variables #=> [:@bar]
Baz.instance_variables #=> []

Anyway, for this simple example you can do this:

无论如何,对于这个简单的例子,你可以这样做:

class Foo
  protected
    def self.add_bar(val)
      @bar ||=[]
      @bar += val
    end
    def self.get_bar
      @bar
    end
end

class Baz < Foo
  add_bar ["a", "b", "c"]
end

Read more about this here.

在此处阅读更多相关信息。

回答by Nicolas Goy

I do it like so:

我这样做:

class Base

    class << self

        attr_accessor :some_var


        def set_some_var(value)
            self.some_var = value
        end

    end

end


class SubClass1 < Base
  set_some_var :foo
end

class SubClass2 < Base
  set_some_var :bar
end

Then it should do what you want.

然后它应该做你想做的。

[8] pry(main)> puts SubClass1.some_var
foo
[9] pry(main)> puts SubClass2.some_var
bar

Note that the set_some_var method is optional, you can do SubClass1.some_var = ...if you prefer.

请注意, set_some_var 方法是可选的,您可以根据需要进行操作SubClass1.some_var = ...

If you want some default value, add something like that under class << self

如果您想要一些默认值,请在下面添加类似的内容 class << self

def some_var
    @some_var || 'default value'
end

回答by Guilherme Bernal

This appears to work well:

这似乎运作良好:

class Foo
  protected
  @@bar = {}
  def self.add_bar(val)
    @@bar[self] ||= []
    @@bar[self] += val
  end
  def self.get_bar
    (self == Foo ? [] : @@bar[Foo] || []) + (@@bar[self] || [])
  end
end
class Baz < Foo
end

Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar     # => ["a", "b"]
Baz.get_bar     # => ["a", "b", "1", "2"]