2014-01-06 2 views
15

Я хотел использовать value.respond_to?(:dup) ? value.dup : value, чтобы проверить, могу ли я дублировать объект, но он не с TypeError на booleans, ноль или такие «примитивы».Как проверить, действительно ли переменная отвечает на: dup?

Я закончил с:

begin 
    value = value.dup 
rescue 
    #ignore, use the original if no dup-able (e.g nil, true, etc) 
end 

Есть ли лучший способ?

Бонус: Почему он отвечает :dup?

Не глубокий dup, только на этот вопрос.

EDIT: Мысли:

  • obj.class.methods.include? :new хорошо, но немного слишком хак я думаю, что это имеет плохую производительность
  • Marshal также выглядит как массовое убийство
  • одна линия спасение могло бы быть лучшим решение, но тип конкретного спасения одной линии невозможен в это время (IIUC matz is on that!), и как @ JörgWMittag упомянул о своем неправильном.
  • Лично я думаю, что dup определяется на уровне объекта неправильно.

Так, цитируя @Linuxios

Существует на самом деле не лучший способ

+0

Что версия рубин вы используете? Я на 1.9.3 и 'value.respond_to? (: Dup)' где значение является логическим, возвращает 'false' мне, а не' type error'. Аналогично нильсам и другим. – Nerve

+0

@Nerve 1.9.3-p484 ... –

+0

FWIW для этого есть открытая проблема, но она не получила большого внимания: https://bugs.ruby-lang.org/issues/11929 –

ответ

5

Существует на самом деле не лучший способ. dup определен в Object, что означает, что любой класс, который хочет не отвечать на него, должен перегрузить его, чтобы выдать исключение. NilClass, TrueClass, FalseClass и Number - все подклассы объекта. Это означает, что они должны переопределить метод, чтобы вызвать ошибку.

В любом случае, если вы ищете глубокую копию, это использовать обычный Marshal.load(Marshal.dump(obj)), который будет обрабатывать номера, bools и nil просто отлично.

Например:

1.9.3-p392 :001 > obj = "hi" 
=> "hi" 
1.9.3-p392 :002 > Marshal.load(Marshal.dump(obj)).object_id != obj.object_id 
=> true 
1.9.3-p392 :003 > obj = 3 
=> 3 
1.9.3-p392 :004 > Marshal.load(Marshal.dump(obj)).object_id != obj.object_id 
=> false 
+1

и 'Marshal.load (Marshal.dump (obj)). Object_id! = Obj.object_id' говорит вам, что это дуп. –

+2

@CarySwoveland: Да, и это настоящая глубокая копия, если только 'obj' - это число, bool или nil. – Linuxios

+0

+1 для использования маршала. Это «хак-иш», но не взлом. –

1

Я считаю, что причина, он реагирует на dup является то, что класс наследует от объекта, который имеет метод dup.

Оказывается, что в методе dup в проверках объектов для «особой постоянной» и поднимает ошибку вы видите:

VALUE 
rb_obj_dup(VALUE obj) 
{ 
    VALUE dup; 

    if (rb_special_const_p(obj)) { 
     rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj)); 
    } 
    dup = rb_obj_alloc(rb_obj_class(obj)); 
    init_copy(dup, obj); 
    rb_funcall(dup, id_init_dup, 1, obj); 

    return dup; 
} 

Я думаю, единственное, что вы можете сделать, это проверить для этих специальных констант в вашем методе.

6

Вы можете написать его в одну строку так:

value = value.dup rescue value 

очень ясно.

Стандарт определяет метод dup, который вызывает TypeError для типов, которые не могут быть дублированы. Таким образом, любой объект будет «реагировать» на него. Вам действительно нужно называть это и проверять с началом-спасением.

+0

+1 Я забыл про одну строку 'спасение'. – Linuxios

+6

Вы должны * никогда не слепо «спасать» от 'StandardError'. Вы должны обрабатывать только конкретное исключение, в данном случае 'TypeError'. –

1
def dupable?(obj) 
    obj.class.methods.include? :new 
end 

dupable?(1)  # => false 
dupable?(3.2) # => false 
dupable?(:a) # => false 
dupable?(true) # => false 
dupable?(nil) # => false 
dupable?("cat") # => true 
+0

wow. творческий. Интересно, охватывает ли он все случаи? –

1

Существует несколько лучший способ, не уверен о проверке сообщения об ошибке, хотя:

begin 
    value = value.dup 
rescue TypeError => e 
    # !!! not sure about the following line 
    raise unless e.message == "can't dup %s" % value.class.name 
    #ignore, use the original if no dup-able (e.g nil, true, etc) 
end 
Смежные вопросы