Добавление переменной экземпляра к классу в Ruby

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

37
задан the Tin Man 8 February 2017 в 22:33
поделиться

6 ответов

Можно использовать средства доступа атрибута:

class Array
  attr_accessor :var
end

Теперь можно получить доступ к нему через:

array = []
array.var = 123
puts array.var
<час>

Примечание, которое можно также использовать attr_reader или attr_writer для определения просто методов get или методы set или Вы могут определить их вручную как таковой:

class Array
  attr_reader :getter_only_method
  attr_writer :setter_only_method

  # Manual definitions equivalent to using attr_reader/writer/accessor
  def var
    @var
  end

  def var=(value)
    @var = value
  end
end
<час>

можно также использовать одноэлементные методы, если Вы просто хотите определенный на единственном экземпляре:

array = []

def array.var
  @var
end

def array.var=(value)
  @var = value
end

array.var = 123
puts array.var
<час>

к вашему сведению, в ответ на комментарий к этому ответу, одноэлементный метод хорошо работает, и следующее является доказательством:

irb(main):001:0> class A
irb(main):002:1>   attr_accessor :b
irb(main):003:1> end
=> nil
irb(main):004:0> a = A.new
=> #<A:0x7fbb4b0efe58>
irb(main):005:0> a.b = 1
=> 1
irb(main):006:0> a.b
=> 1
irb(main):007:0> def a.setit=(value)
irb(main):008:1>   @b = value
irb(main):009:1> end
=> nil
irb(main):010:0> a.setit = 2
=> 2
irb(main):011:0> a.b
=> 2
irb(main):012:0> 

, Как Вы видите, одноэлементный метод setit установит то же поле, @b, как тот определил использование attr_accessor..., таким образом, одноэлементный метод является совершенно допустимым подходом к этому вопросу.

15
ответ дан the Tin Man 27 November 2019 в 04:16
поделиться

Ruby предоставляет методы для этого, instance_variable_get и instance_variable_set. ( документы )

можно создать и присвоить новые переменные экземпляра как это:

>> foo = Object.new
=> #<Object:0x2aaaaaacc400>

>> foo.instance_variable_set(:@bar, "baz")
=> "baz"

>> foo.inspect
=> #<Object:0x2aaaaaacc400 @bar=\"baz\">
65
ответ дан Gordon Wilson 27 November 2019 в 04:16
поделиться

Только для чтения

, Если Ваше использование "класса MyObject" является использованием открытого класса, то, обратите внимание переопределение инициализировать метода.

В Ruby, нет такой вещи как перегрузка... только переопределения или переопределения..., другими словами, может только быть 1 экземпляр любого данного метода, поэтому при переопределении его это переопределено..., и инициализировать метод не отличается (даже при том, что это что новый метод использования Объектов класса).

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

Во всяком случае, я думаю, что у меня есть много простого решения для Вас, которое использует фактический метакласс для определения одноэлементных методов:

m = MyObject.new
metaclass = class << m; self; end
metaclass.send :attr_accessor, :first, :second
m.first = "first"
m.second = "second"
puts m.first, m.second

можно использовать обоих метакласс и открыть классы, чтобы стать еще более хитрыми и сделать что-то как:

class MyObject
  def metaclass
    class << self
      self
    end
  end

  def define_attributes(hash)
    hash.each_pair { |key, value|
      metaclass.send :attr_accessor, key
      send "#{key}=".to_sym, value
    }
  end
end

m = MyObject.new
m.define_attributes({ :first => "first", :second => "second" })

Вышеупомянутое в основном представляет метакласс с помощью метода "метакласса", затем с помощью него в define_ атрибуты для динамичного определения набора атрибутов с attr_ средство доступа и затем вызов метода set атрибута впоследствии с присваиваемым значением в хеше.

С Ruby можно стать творческими и сделать то же самое много различных путей;-)

<час>

к вашему сведению, в случае, если Вы не знали, с помощью метакласса, поскольку я сделал средства, Вы только действуете на приведенный пример объекта. Таким образом, вызов define_ атрибуты только определят те атрибуты для того конкретного экземпляра.

Пример:

m1 = MyObject.new
m2 = MyObject.new
m1.define_attributes({:a => 123, :b => 321})
m2.define_attributes({:c => "abc", :d => "zxy"})
puts m1.a, m1.b, m2.c, m2.d # this will work
m1.c = 5 # this will fail because c= is not defined on m1!
m2.a = 5 # this will fail because a= is not defined on m2!
15
ответ дан Community 27 November 2019 в 04:16
поделиться

ответ Mike Stone является уже довольно всесторонним, но я хотел бы добавить немного детали.

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

s1 = 'string 1'
s2 = 'string 2'

class String
  attr_accessor :my_var
end

s1.my_var = 'comment #1'
s2.my_var = 'comment 2'

puts s1.my_var, s2.my_var
2
ответ дан Community 27 November 2019 в 04:16
поделиться

Другие решения будут работать отлично также, но здесь являются примером с помощью define_ метод, если Вы одержимы не использованием открытых классов..., оно определит переменную "var" для класса массива..., но отметит, что это ЭКВИВАЛЕНТНО использованию открытого класса..., преимущество - Вы, может сделать это для неизвестного класса (так класс любого объекта, вместо того, чтобы открыть определенный класс)... также define_ метод будет работать в методе, тогда как Вы не можете открыть класс в рамках метода.

array = []
array.class.send(:define_method, :var) { @var }
array.class.send(:define_method, :var=) { |value| @var = value }

И вот пример, он - использование... отмечают, что array2, ОТЛИЧАЮЩИЙСЯ массив также имеет методы, поэтому если это не то, что Вы хотите, Вы, вероятно, хотите одноэлементные методы, которые я объяснил в другом сообщении.

irb(main):001:0> array = []
=> []
irb(main):002:0> array.class.send(:define_method, :var) { @var }
=> #<Proc:0x00007f289ccb62b0@(irb):2>
irb(main):003:0> array.class.send(:define_method, :var=) { |value| @var = value }
=> #<Proc:0x00007f289cc9fa88@(irb):3>
irb(main):004:0> array.var = 123
=> 123
irb(main):005:0> array.var
=> 123
irb(main):006:0> array2 = []
=> []
irb(main):007:0> array2.var = 321
=> 321
irb(main):008:0> array2.var
=> 321
irb(main):009:0> array.var
=> 123
2
ответ дан Mike Stone 27 November 2019 в 04:16
поделиться

Только для чтения, в ответ на Ваше редактирование:

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

я думаю, что Вы не вполне понимаете понятия "открытых классов", что означает, что можно открыть класс в любое время. Например:

class A
  def hello
    print "hello "
  end
end

class A
  def world
    puts "world!"
  end
end

a = A.new
a.hello
a.world

Вышеупомянутое является совершенно допустимым кодом Ruby, и эти 2 определения классов могут быть распространены через несколько файлов Ruby. Вы могли использовать "define_method" метод в объекте Модуля определить новый метод на экземпляре класса, но это эквивалентно использованию открытых классов.

"Открытые классы" в Ruby означает, что можно переопределить ЛЮБОЙ класс в ЛЮБОМ моменте времени..., что означает, добавляют новые методы, переопределяют существующие методы, или независимо от того, что Вы хотите действительно. Это кажется, что "открытый класс" решение действительно - то, что Вы ищете...

0
ответ дан Mike Stone 27 November 2019 в 04:16
поделиться