Как я могу получить доступ к переменной, которая определена в области видимости с помощью строчных символов? - PullRequest
0 голосов
/ 30 апреля 2020

Предположим, у меня есть следующий класс, как я могу получить доступ к переменной var:

class Test
  var = "value"

  def m1
    var
  end

  def self.m2
    var
  end
end

Я пробовал эти способы, но они не увенчались успехом:

Test.new.m1  #Error
Test.m2      #Error
Test.var     #Error
Test::var    #Error

ПРИМЕЧАНИЕ: Я не имею в виду определять экземпляр или переменную класса, мне просто любопытно, как это происходит.

Ответы [ 2 ]

1 голос
/ 01 мая 2020

Предположим, у меня есть следующий класс, как я могу получить доступ к переменной var

Переменные, идентификатор которых начинается со строчной буквы, локальные переменные . Локальные переменные являются локальными для области, в которой они определены, поэтому они называются «локальными» переменными.

Заданная вами переменная - это просто скучная старая стандартная локальная переменная, как и любая другая локальная переменная. В этом нет ничего особенного или особенного. К нему можно получить доступ, как и к любой другой локальной переменной: просто записав ее имя.

Например:

class Test
  var = "value"

  puts var # See? Nothing special, just like any other local variable.
end
# value

Последнее выражение, вычисленное внутри тела определения модуля, является значением модуля. определение выражения, чтобы вы могли извлечь содержимое переменной следующим образом:

another_local_variable = class Test
  var = "value"

  var
end

another_local_variable
#=> "value"

Вы также можете получить доступ к локальной переменной во вложенных областях. Опять же, в этом нет абсолютно ничего особенного или отличного. Это просто локальная переменная, которая ведет себя точно , как и любая другая локальная переменная, которую вы, возможно, уже использовали.

Единственные конструкции в Ruby, которые создают вложенные области видимости, - это блоки и лямбда-литералы. Итак, вы можете сделать что-то вроде этого:

class Test
  var = "value"

  define_method(:m1)           do var end
  define_singleton_method(:m2) do var end
end

Test.new.m1 #=> "value"
Test.m2     #=> "value"

Если вы можете получить доступ к объекту Binding (используя метод Kernel#binding) для лексическая область действия, в которой определена локальная переменная, затем вы можете получить ее значение, используя Binding#local_variable_get:

b = class Test
  var = "value"

  binding
end

b.local_variable_get(:var) #=> "value"

Но опять же, я должен повторить: ничто в этом нет ничего особенного. Эта локальная переменная ведет себя на 100% точно так же, как любая другая локальная переменная . Локальная переменная - это просто локальная переменная.

Локальные переменные очень полезны в Ruby, потому что они являются языковой конструкцией only , которая обеспечивает идеальную инкапсуляцию. Все другие средства инкапсуляции в Ruby (переменные экземпляра, private и т. Д. c.) Могут быть обойдены с помощью отражение (например, с использованием Object#instance_variable_get или Object#send), и отражение всегда доступно каждому без контроля доступа.

Но если кто-то явно не предоставит вам объект Binding, локальные переменные не могут быть доступным за пределами их локального охвата. Раньше это было очень полезно для реализации этого шаблона:

class Foo
  def bar
    'Hello'
  end
end 

class Foo
  old_bar = instance_method(:bar)

  define_method(:bar) do
    old_bar.bind(self).() + ' World'
  end
end

Foo.new.bar # => 'Hello World'

Вы можете узнать больше об этом шаблоне в этом моем ответе под заголовком Метод Wrapping : { ссылка }

Несмотря на то, что этот специфический c шаблон устарел с введением Module#prepend, существуют другие подобные ситуации, где он все еще полезен, например, для запоминания.

0 голосов
/ 01 мая 2020

Насколько я знаю, вы не можете сделать это так, как вы предлагали выше.

Но вы можете сделать несколько довольно сумасшедших вещей, как показано ниже, чтобы получить эти переменные.

class Test

  # This can only be accessed within the class.
  var = "value"

  def initialize
    @hello = "hello world!"
  end

  def m1
    var # you won't be able to access var here
  end

  def self.m2
    var
  end

  private # note the following method is private

  def n1
    "hello n1"
  end

end

t = Test.new

# puts t.m1 # =>  `m1': undefined local variable or method `var' for #<Test:0x0000563c68688a58>

# t.send(:m1) # =>  `m1': undefined local variable or method `var' for #<Test:0x0000563c68688a58>

# we can still access the private method like this:

puts t.send(:n1) # => hello n1

puts t.instance_variable_get("@hello") # => "hello world"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...