class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
cho tôi lỗi:
Cú pháp: lỗi gán hằng số động
Tại sao điều này được coi là một hằng số động? Tôi chỉ gán một chuỗi cho nó.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
cho tôi lỗi:
Cú pháp: lỗi gán hằng số động
Tại sao điều này được coi là một hằng số động? Tôi chỉ gán một chuỗi cho nó.
Câu trả lời:
Vấn đề của bạn là mỗi lần bạn chạy phương thức bạn đang gán một giá trị mới cho hằng số. Điều này không được phép, vì nó làm cho hằng số không đổi; mặc dù nội dung của chuỗi là như nhau (hiện tại, dù sao đi nữa), bản thân đối tượng chuỗi thực tế sẽ khác nhau mỗi khi phương thức được gọi. Ví dụ:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Có lẽ nếu bạn giải thích trường hợp sử dụng của bạn, tại sao bạn muốn thay đổi giá trị của hằng số trong một phương thức thì chúng tôi có thể giúp bạn thực hiện tốt hơn.
Có lẽ bạn muốn có một biến đối tượng trên lớp?
class MyClass
class << self
attr_accessor :my_constant
end
def my_method
self.class.my_constant = "blah"
end
end
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
Nếu bạn thực sự muốn thay đổi giá trị của hằng số trong một phương thức và hằng số của bạn là Chuỗi hoặc Mảng, bạn có thể 'gian lận' và sử dụng #replace
phương thức để khiến đối tượng nhận một giá trị mới mà không thực sự thay đổi đối tượng:
class MyClass
BAR = "blah"
def cheat(new_bar)
BAR.replace new_bar
end
end
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. Đó là một trong những trường hợp mà Ruby không có cách đơn giản.
@variable
), không phải là hằng số. Nếu không, bạn sẽ được chỉ định lại DB
mỗi khi bạn khởi tạo một thể hiện mới của lớp đó.
Sequel.connect
cho một hằng số có tên DB . Trong thực tế, tài liệu nói rõ ràng rằng đó chỉ là một khuyến nghị. Điều đó không giống như một ràng buộc bên ngoài đối với tôi.
Vì các hằng số trong Ruby không có nghĩa là phải thay đổi, Ruby không khuyến khích bạn gán cho chúng trong các phần của mã có thể được thực thi nhiều lần, chẳng hạn như các phương thức bên trong.
Trong trường hợp bình thường, bạn nên định nghĩa hằng số bên trong lớp:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
Nếu vì một lý do nào đó mặc dù bạn thực sự cần xác định một hằng trong một phương thức (có lẽ đối với một số loại siêu lập trình), bạn có thể sử dụng const_set
:
class MyClass
def my_method
self.class.const_set(:MY_CONSTANT, "foo")
end
end
MyClass::MY_CONSTANT
#=> NameError: uninitialized constant MyClass::MY_CONSTANT
MyClass.new.my_method
MyClass::MY_CONSTANT #=> "foo"
Một lần nữa, const_set
không phải là thứ bạn thực sự phải dùng đến trong những trường hợp bình thường. Nếu bạn không chắc chắn liệu bạn có thực sự muốn gán cho hằng số theo cách này hay không, bạn có thể muốn xem xét một trong những lựa chọn thay thế sau:
Các biến lớp hoạt động như hằng số theo nhiều cách. Chúng là các thuộc tính trên một lớp và chúng có thể truy cập được trong các lớp con của lớp mà chúng được định nghĩa.
Sự khác biệt là các biến lớp có nghĩa là có thể sửa đổi và do đó có thể được gán cho các phương thức bên trong mà không có vấn đề gì.
class MyClass
def self.my_class_variable
@@my_class_variable
end
def my_method
@@my_class_variable = "foo"
end
end
class SubClass < MyClass
end
MyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"
Các thuộc tính lớp là một loại "biến thể hiện trên một lớp". Chúng hoạt động hơi giống các biến lớp, ngoại trừ giá trị của chúng không được chia sẻ với các lớp con.
class MyClass
class << self
attr_accessor :my_class_attribute
end
def my_method
self.class.my_class_attribute = "blah"
end
end
class SubClass < MyClass
end
MyClass.my_class_attribute #=> nil
SubClass.my_class_attribute #=> nil
MyClass.new.my_method
MyClass.my_class_attribute #=> "blah"
SubClass.my_class_attribute #=> nil
SubClass.new.my_method
SubClass.my_class_attribute #=> "blah"
Và để hoàn thiện, có lẽ tôi nên đề cập: nếu bạn cần gán một giá trị chỉ có thể được xác định sau khi lớp của bạn được khởi tạo, có nhiều khả năng bạn thực sự đang tìm kiếm một biến thể cũ đơn giản.
class MyClass
attr_accessor :instance_variable
def my_method
@instance_variable = "blah"
end
end
my_object = MyClass.new
my_object.instance_variable #=> nil
my_object.my_method
my_object.instance_variable #=> "blah"
MyClass.new.instance_variable #=> nil
Trong Ruby, bất kỳ biến nào có tên bắt đầu bằng chữ in hoa là một hằng số và bạn chỉ có thể gán cho nó một lần. Chọn một trong những lựa chọn thay thế sau:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Các hằng số trong ruby không thể được định nghĩa bên trong các phương thức. Xem ghi chú ở dưới cùng của trang này, ví dụ
Ruby không thích rằng bạn đang gán hằng số bên trong một phương thức vì nó có nguy cơ gán lại. Một số câu trả lời SO trước khi tôi đưa ra phương án gán nó bên ngoài một phương thức - nhưng trong lớp, đó là nơi tốt hơn để gán nó.
Rất cám ơn Dorian và Phrogz đã nhắc nhở tôi về phương thức mảng (và hàm băm) #replace, có thể "thay thế nội dung của một mảng hoặc hàm băm".
Quan niệm rằng giá trị của CONSTANT có thể được thay đổi, nhưng với một cảnh báo khó chịu, là một trong những bước sai lầm về khái niệm của Ruby - những điều này nên hoàn toàn bất biến, hoặc loại bỏ hoàn toàn ý tưởng không đổi. Từ quan điểm của một lập trình viên, một hằng số là khai báo và cố ý, một tín hiệu cho người khác rằng "giá trị này thực sự không thể thay đổi một khi được khai báo / gán."
Nhưng đôi khi một "tuyên bố rõ ràng" thực sự bị tịch thu các cơ hội hữu ích khác trong tương lai. Ví dụ...
có rất trường hợp sử dụng hợp pháp trong đó giá trị "hằng số" có thể thực sự cần phải thay đổi: ví dụ: tải lại ARGV từ vòng lặp giống như REPL, sau đó chạy lại ARGV qua nhiều hơn (tiếp theo) OptionParser.parse! gọi - thì đấy! Cung cấp "dòng lệnh lập luận" một tiện ích động hoàn toàn mới.
Vấn đề thực tế là một trong hai với giả định giả định rằng "argv phải là một hằng số", hoặc trong phương thức khởi riêng optparse, mà cứng mã sự phân công của argv đến dụ var @default_argv cho sau này xử lý - mà mảng (argv) thực sự nên là một tham số, khuyến khích phân tích lại và sử dụng lại, khi thích hợp. Tham số hóa phù hợp, với một mặc định thích hợp (giả sử ARGV) sẽ tránh được việc phải thay đổi ARGV "không đổi". Chỉ cần một vài suy nghĩ xứng đáng ...