2012-04-04 5 views

Trả lời

24

Sử dụng nội tâm, Luke!

class A 
    attr_accessor :x, :y 

    def initialize(*args) 
    @x, @y = args 
    end 

    def attrs 
    instance_variables.map{|ivar| instance_variable_get ivar} 
    end 
end 

a = A.new(5,10) 
a.x # => 5 
a.y # => 10 
a.attrs # => [5, 10] 
+2

Lưu ý: attrs sẽ trở lại * tất cả * các biến mẫu, thay vì chỉ các biến được hiển thị bởi 'attr_accessor' – Jonah

+0

@Jonah: vâng, đó là giả thiết lúc đó. Đối với một phương pháp chính xác hơn, người ta có thể tham khảo [câu trả lời này] (http://stackoverflow.com/a/34440466/125816). –

1

khi bạn sử dụng attr_accessor để xác định thuộc tính trong một lớp học, Ruby sử dụng refexion, xác định một vài phương pháp, đối với mỗi thuộc tính khai báo, một để có được những giá trị và khác để thiết lập, một biến thể hiện của cùng tên của thuộc tính

bạn có thể thấy phương pháp này sử dụng

p A.instance_methods 

[:x, :x=, :y, :y=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?,.. 

Vì vậy, thuộc tính này đều lấy, bên ngoài lớp học, với

p "#{a.x},#{a.y}" 

hoặc bên trong lớp thông qua các ví dụ tương ứng biến

class A 
    ... 
    def attributes 
    [@x,@y] 
    end 
    ... 
end 
p a.attributes #=> [5,10] 
+3

Cảm ơn phản hồi của bạn, nhưng tôi đã hỏi về một trường hợp khi tôi không biết tên của các thuộc tính, bởi vì nếu không nó sẽ không được tiếp cận DRY.Ví dụ: nếu tôi quyết định thêm thuộc tính bổ sung '@ d',' @ e', '@ f'. Sau đó, tôi sẽ phải thêm chúng trong 'manual' phương thức manualy cũng như trong' initialize', dẫn đến việc sao chép mã. – user1179942

+0

Trong tình huống này, hãy sử dụng giải pháp của Sergio – pgon

12

Trong khi câu trả lời Sergio giúp, nó sẽ trở lại tất cả các biến Ví dụ, mà nếu tôi hiểu đúng câu hỏi của OP, không phải là những gì được yêu cầu.

Nếu bạn chỉ muốn trả lại 'thuộc tính' có, ví dụ: một mutator, bạn phải làm một cái gì đó hơi phức tạp hơn như:

attrs = Hash.new 
instance_variables.each do |var| 
    str = var.to_s.gsub /^@/, '' 
    if respond_to? "#{str}=" 
    attrs[str.to_sym] = instance_variable_get var 
    end 
end 
attrs 

này chỉ trả về các thuộc tính khai báo với attr_accessor (hoặc với một mutator tự tạo), và giữ các biến nội bộ ẩn. Bạn có thể làm một cái gì đó tương tự nếu bạn muốn những cái được khai báo với attr_reader.

+0

Tôi nghĩ đây là câu trả lời hay hơn cho câu hỏi. Tôi sẽ nối thêm vào nó. Ruby tạo {Variable} = phương thức bất cứ khi nào một biểu tượng được thêm làm bộ truy cập hoặc trình ghi. đáp ứng? chỉ cần đảm bảo rằng có một phương thức hợp lệ tồn tại với tên {Variable} =. Phần đầu tiên là để chomp off "@" đầu tiên trên biến trả về. Trong ví dụ OP, các phương thức "x =" và "y =" sẽ được tạo và bạn đang tìm kiếm chúng bằng @x; x = và @y; y = –

+0

Trong một số trường hợp, điều này sẽ bao gồm ': attributes = '. : / – XtraSimplicity

3

Xem mục khác Stack Overflow Question. Họ ghi đè attr_accessor.

def self.attr_accessor(*vars) 
    @attributes ||= [] 
    @attributes.concat vars 
    super(*vars) 
    end 

    def self.attributes 
    @attributes 
    end 

    def attributes 
    self.class.attributes 
    end 
1
class A 
    ATTRIBUTES = [:x, :y] 
    attr_accessor *ATTRIBUTES 

    def initialize(x,y) 
    @x, @y = x, y 
    end 

    def attributes 
    ATTRIBUTES.map{|attribute| self.send(attribute) } 
    end 
end 

này có thể không phải DRY-est, nhưng nếu bạn chỉ quan tâm đến việc này cho một lớp (như trái ngược với một lớp cơ sở mà tất cả mọi thứ được thừa hưởng từ), sau đó điều này sẽ làm việc.

0

Nếu bạn có attr_writer s/attr_accessor s xác định trên thuộc tính của bạn, hơn những gì họ có thể dễ dàng lấy ra bằng cách kết hợp các =$ regexp:

A.instance_methods.each_with_object([]) { |key, acc| acc << key.to_s.gsub(/=$/, '') if key.match(/\w=$/) } 

HOẶC

A.instance_methods.each_with_object([]) { |key, acc| acc << key if key = key.to_s.match(/^(.*\w)=$/)&.[](1) }