2009-05-05 22 views
61
class Hello 
@hello = "hello" 
    def display 
     puts @hello 
    end 
end 

h = Hello.new 
h.display 

Tôi đã tạo lớp ở trên. Nó không in ra bất cứ thứ gì. Tôi nghĩ biến cá thể @hello được đặt trong quá trình khai báo lớp. Nhưng khi tôi gọi phương thức hiển thị đầu ra là 'nil'. Cách chính xác để làm điều này là gì?Khi nào các biến mẫu của Ruby được đặt?

Trả lời

88

biến Instance trong ruby ​​có thể là một chút bối rối khi lần đầu tiên học Ruby, đặc biệt là nếu bạn đang quen với một ngôn ngữ khác như Java OO .

Bạn không thể chỉ đơn giản khai báo một biến mẫu.

Một trong những điều quan trọng nhất cần biết về biến mẫu trong ruby, ngoài ký hiệu có tiền tố dấu @, là chúng xuất hiện trong lần đầu tiên được gán cho.

class Hello 
    def create_some_state 
    @hello = "hello" 
    end 
end 

h = Hello.new 
p h.instance_variables 

h.create_some_state 
p h.instance_variables 

# Output 
[] 
["@hello"] 

Bạn có thể sử dụng phương pháp Object#instance_variables để liệt kê tất cả các biến mẫu của đối tượng.

Bạn thường “khai báo” và khởi tạo tất cả các biến mẫu trong phương thức khởi tạo. Một cách khác để ghi rõ các biến mẫu nào cần được công bố công khai là sử dụng các phương thức Mô-đun attr_accessor (đọc/ghi), attr_writer (ghi) và attr_reader (đọc). Các phương thức này sẽ tổng hợp các phương thức truy cập khác nhau cho biến cá thể được liệt kê.

class Hello 
    attr_accessor :hello 
end 

h = Hello.new 
p h.instance_variables 

h.hello = "hello" 
p h.instance_variables 

# Output 
[] 
["@hello"] 

Biến thể hiện vẫn chưa được tạo cho đến khi được chỉ định sử dụng phương pháp tổng hợp Hello#hello=.

Một vấn đề quan trọng khác, như kch được mô tả, là bạn cần phải biết các ngữ cảnh khác nhau đang hoạt động khi khai báo một lớp. Khi khai báo một lớp, bộ nhận mặc định (tự) trong phạm vi ngoài cùng sẽ là đối tượng đại diện cho chính lớp đó. Do đó mã của bạn trước tiên sẽ tạo ra một biến cá thể lớp khi gán cho @hello ở cấp lớp.

Phương thức bên trong tự sẽ là đối tượng mà phương thức được gọi, do đó bạn đang cố in giá trị của biến mẫu bằng tên @hello trong đối tượng không tồn tại (lưu ý rằng nó hoàn toàn hợp pháp để đọc một biến cá thể không tồn tại).

+2

Bạn nói rằng "chúng sinh ra trong cuộc sống lần đầu tiên chúng được chỉ định", nhưng OP cho thấy một ví dụ với sự phân công rõ ràng sớm hơn trong ví dụ của bạn, và vấn đề có kinh nghiệm là biến đã không nảy sinh vào cuộc sống. – kaleidic

+1

@kaleidic Chính xác. Tôi hơi bối rối về số lượng upvotes trong câu trả lời này không giải quyết được câu hỏi của OP. Trong thực tế, biến cá thể lớp '@ hello' * làm * spring vào cuộc sống trên hàng 2 của mã ví dụ, nhưng vấn đề là không phải là biến mà hàng 4 đề cập đến. Xem câu trả lời của tôi dưới đây để biết thêm chi tiết. –

+0

Xin lỗi, bạn thực sự trả lời câu hỏi ở phần cuối, điều mà tôi đã bỏ lỡ khi đọc lần đầu tiên. –

42

Bạn cần phải thêm một phương pháp initialize:

class Hello 
    def initialize 
     @hello = "hello" 
    end 
    def display 
     puts @hello 
    end 
end 

h = Hello.new 
h.display 
21

Đầu tiên @hello trong mã của bạn được gọi là một biến lớp ví dụ.

Đây là biến mẫu của đối tượng lớp mà hằng số Hello trỏ đến. (Và đó là một thể hiện của lớp Class.)

Về mặt kỹ thuật, khi bạn đang ở trong phạm vi class, bạn self được thiết lập để các đối tượng của lớp hiện tại của bạn, và @variables liên quan đến hiện tại self của bạn. Cậu bé tôi thích giải thích những điều này.

Bạn có thể nhận được tất cả điều này và được làm rõ hơn cho bạn bằng cách xem this collection of $5-each screencasts from The Pragmatic Programmers.

(Hoặc bạn có thể yêu cầu làm rõ ở đây và tôi sẽ cố gắng cập nhật.)

+0

Một bài viết hay (http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/) xây dựng trên các biến cá thể cấp lớp. –

9

có mô tả rõ ràng trong sách "Ngôn ngữ lập trình ruby", đọc nó sẽ rất hữu ích. Tôi dán nó ở đây (từ chương 7.1.16):

Biến dụ sử dụng bên trong một định nghĩa lớp nhưng ngoài một định nghĩa phương pháp dụ là một trường hợp lớp biến.

class Point 
    # Initialize our class instance variables in the class definition itself 
    @n = 0    # How many points have been created 
    @totalX = 0   # The sum of all X coordinates 
    @totalY = 0   # The sum of all Y coordinates 

    def initialize(x,y) # Initialize method 
     @x,@y = x, y  # Sets initial values for instance variables 
    end 

    def self.new(x,y) # Class method to create new Point objects 
     # Use the class instance variables in this class method to collect data 
     @n += 1   # Keep track of how many Points have been created 
     @totalX += x  # Add these coordinates to the totals 
     @totalY += y 

     super    # Invoke the real definition of new to create a Point 
        # More about super later in the chapter 
    end 

    # A class method to report the data we collected 
    def self.report 
     # Here we use the class instance variables in a class method 
     puts "Number of points created: #@n" 
     puts "Average X coordinate: #{@totalX.to_f/@n}" 
     puts "Average Y coordinate: #{@totalY.to_f/@n}" 
    end 
end 

......

Bởi vì các biến lớp dụ chỉ là biến thể hiện của lớp các đối tượng, chúng ta có thể sử dụng attr, attr_reader, và attr_accessor để tạo phương pháp accessor cho họ .

class << self 
    attr_accessor :n, :totalX, :totalY 
end 

Với những bộ truy xuất được xác định, chúng ta có thể tham khảo dữ liệu thô của chúng tôi như Point.n, Point.totalX, và Point.totalY.

1

tôi cũng khuyên bạn nên nhìn vào các biến lớp học mà được bắt đầu với "@@" - đây là một số mẫu mã để chỉ cho bạn cách lớp và dụ vars là khác nhau:

class Vars 
    @@classvar="foo" 
    def test 
    @instancevar="bar" 
    end 
    def Vars.show 
    puts "classvar: #{@@classvar}" 
    puts "instancevar: #{@instancevar}" 
    end 
    def instance_show 
    puts "classvar: #{@@classvar}" 
    puts "instancevar: #{@instancevar}" 

    end 
end 

# only shows classvar since we don't have an instance created 
Vars::show 
# create a class instance 
vars = Vars.new 
# instancevar still doesn't show b/c it hasn't been initialized 
vars.instance_show 
# initialize instancevar 
vars.test 
# now instancevar shows up as we expect 
vars.instance_show 
4

Tôi đã quên rằng có là một khái niệm "biến thể hiện lớp" trong Ruby. Trong mọi trường hợp, vấn đề của OP có vẻ khó hiểu, và không thực sự được giải quyết trong bất kỳ câu trả lời nào trước đây, ngoại trừ một gợi ý trong câu trả lời của kch: đó là một vấn đề về phạm vi. (Được thêm vào chỉnh sửa: Trên thực tế, câu trả lời của sris không giải quyết vấn đề này vào lúc nào, nhưng tôi sẽ cho phép câu trả lời này đứng yên, vì tôi nghĩ mã ví dụ có thể hữu ích để hiểu vấn đề.)

In a Lớp Ruby, tên biến số bắt đầu bằng @ có thể tham chiếu đến một trong số hai biến số: hoặc là biến thể hiện hoặc đến một biến thể hiện lớp , tùy thuộc vào vị trí của lớp. Đây là một hình ảnh khá tinh tế.

Ví dụ sẽ làm rõ điểm. Dưới đây là một chút lớp của Ruby thử nghiệm (tất cả các mã thử nghiệm trong IRB):

class T 

    @@class_variable = "BBQ" 
    @class_instance_variable_1 = "WTF" 
    @class_instance_variable_2 = "LOL" 

    def self.class_method 
    puts "@@class_variable   == #{@@class_variable   || 'nil'}" 
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}" 
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}" 
    puts "@instance_variable   == #{@instance_variable   || 'nil'}" 
    end 

    def initialize 
    @instance_variable = "omg" 
    # The following line does not assign a value to the class instance variable, 
    # but actually declares an instance variable withthe same name! 
    @class_instance_variable_1 = "wtf" 
    puts "@@class_variable   == #{@@class_variable   || 'nil'}" 
    # The following two lines do not refer to the class instance variables, 
    # but to the instance variables with the same names. 
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}" 
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}" 
    puts "@instance_variable   == #{@instance_variable   || 'nil'}" 
    end 

    def instance_method 
    puts "@@class_variable   == #{@@class_variable   || 'nil'}" 
    # The following two lines do not refer to the class instance variables, 
    # but to the instance variables with the same names. 
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}" 
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}" 
    puts "@instance_variable   == #{@instance_variable   || 'nil'}" 
    end 

end 

tôi đặt tên các biến theo những gì tôi nghĩ họ, dù rằng hóa ra không phải luôn luôn là trường hợp:

irb> T.class_method 
@@class_variable   == BBQ 
@class_instance_variable_1 == WTF # the value of the class instance variable 
@class_instance_variable_2 == LOL # the value of the class instance variable 
@instance_variable   == nil # does not exist in the class scope 
=> nil 

irb> t = T.new 
@@class_variable   == BBQ 
@class_instance_variable_1 == wtf # the value of the instance variable 
@class_instance_variable_2 == nil # the value of the instance variable 
@instance_variable   == omg 
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf"> 

irb> t.instance_method 
@@class_variable   == BBQ 
@class_instance_variable_1 == wtf # the value of the instance variable 
@class_instance_variable_2 == nil # the value of the instance variable 
@instance_variable   == omg 
=> nil 

irb> T.class_method 
@@class_variable   == BBQ 
@class_instance_variable_1 == WTF # the value of the class instance variable 
@class_instance_variable_2 == LOL # the value of the class instance variable 
@instance_variable   == nil # does not exist in the class scope 
=> nil 

@@class_variable@instance_variable luôn hoạt động như bạn mong muốn: giá trị cũ được xác định ở cấp lớp và được tham chiếu trong phương thức lớp hoặc phương pháp thể hiện, nó giữ giá trị được gán cho nó ở trên cùng. Cái sau chỉ nhận được một giá trị trong một đối tượng của lớp T, vì vậy trong một phương thức lớp, nó chỉ một biến không xác định có giá trị là nil.

Phương pháp lớp có tên là class_method xuất ra các giá trị @@class_variable và hai số @class_instance_variable s như mong đợi, tức là, như được khởi tạo ở đầu lớp. Tuy nhiên, trong các phương pháp ví dụ initializeinstance_method, khác nhau biến cùng tên được truy cập, nghĩa là, biến mẫu, không phải biến thể hiện lớp.

Bạn có thể thấy rằng sự phân công trong phương pháp initialize không ảnh hưởng đến cá thể lớp biến @class_instance_variable_1, vì cuộc gọi sau này của class_method kết quả đầu ra giá trị cũ của nó, "WTF". Thay vào đó, phương pháp initializeđã khai báo một biến mẫu mới, một trong đó là cũng có tên (misleadingly) @class_instance_variable_1. Giá trị được gán cho nó, "wtf", được xuất theo phương thức initializeinstance_method.

Biến @class_instance_variable_2 trong mã ví dụ là tương đương với biến @hello trong vấn đề ban đầu: nó khai báo và khởi tạo như là một biến lớp Chẳng hạn, nhưng khi một phương pháp dụ đề cập đến một biến của tên đó, nó thực sự thấy một ví dụ biến có cùng tên - một trong số đó chưa bao giờ được khai báo, vì vậy giá trị của nó là không.

+0

chỉ có câu trả lời này có ý nghĩa :) – InQusitive

+0

@InQusitive Vui mừng được trợ giúp! –

Các vấn đề liên quan