Bắt đầu xung quanh sự bất biến


13

Tôi chưa quen với lập trình hướng đối tượng và một khái niệm khiến tôi mất một thời gian để nắm bắt là sự bất biến. Tôi nghĩ rằng bóng đèn đã tắt đêm qua nhưng tôi muốn xác minh:

Khi tôi bắt gặp những tuyên bố rằng một đối tượng bất biến không thể thay đổi, tôi rất bối rối vì tôi có thể làm như sau:

NSString *myName = @"Bob";
myName = @"Mike";

Ở đó, tôi vừa thay đổi myName, loại NSString bất biến. Vấn đề của tôi là từ, "đối tượng" có thể chỉ đối tượng vật lý trong bộ nhớ hoặc trừu tượng hóa, "myName." Định nghĩa trước đây áp dụng cho khái niệm bất biến.

Đối với biến, một định nghĩa rõ ràng hơn (với tôi) về tính bất biến là giá trị của một đối tượng bất biến chỉ có thể được thay đổi bằng cách thay đổi vị trí của nó trong bộ nhớ, tức là tham chiếu của nó (còn được gọi là con trỏ của nó).

Điều này có đúng không, hay tôi vẫn lạc trong rừng?


13
Kiểu của bạn không phải là một NSString, nó là " con trỏ tớiNSString", không phải là bất biến. Tôi không biết gì về mục tiêu C, nhưng tôi đoán trong ví dụ của bạn @"Mike"đang tạo một thể hiện mới NSStringvà gán nó cho con trỏ , myName. Vì vậy, bạn đã không thay đổi đối tượng myNameđược trỏ đến, chỉ những gì nó được trỏ đến.
fwgx

2
@fwgx Đặt nó làm câu trả lời và bạn sẽ nhận được upvote của tôi.
Gul Sơn

Cảm ơn tất cả mọi người vì tất cả các câu trả lời, chúng rất hữu ích. Bây giờ tôi hiểu rằng đối tượng bất biến là giá trị trong bộ nhớ, không phải là biến chỉ đến nó.
Michael Mangold

@Gulshan Xong, xem bên dưới ....
fwgx

Để hiểu tính bất biến, tôi khuyên bạn nên học một ngôn ngữ lập trình phân tách rõ ràng ràng buộc (nghĩa là đặt tên cho các đối tượng) với khả năng biến đổi (nghĩa là cho phép một tên được gán lại cho một đối tượng khác). ML (trong bất kỳ chiêu bài: SML, Ocaml, F #) là một ví dụ điển hình.
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


4

Nghe có vẻ như bạn đang đi đúng hướng, nhưng vẫn chưa hiểu rõ. Cái này sai:

Ở đó, tôi vừa thay đổi myName, loại NSString bất biến. Vấn đề của tôi là từ, "đối tượng" có thể chỉ đối tượng vật lý trong bộ nhớ hoặc trừu tượng hóa, "myName."

Trong đoạn mã của bạn, myNamekhông phải là loại bất biến NSString, nó là loại có thể thay đổiNSString* (con trỏ tới NSString). Nghe có vẻ như điều quan trọng mà bạn thiếu là hiểu rằng một con trỏ chỉ là một giá trị khácnó có một cuộc sống hoàn toàn tách biệt với thứ mà nó chỉ ra (hoặc những thứ, nếu bạn thay đổi nó một phần trong suốt vòng đời của nó).

Bạn nói:

... Giá trị của một đối tượng bất biến chỉ có thể được thay đổi bằng cách thay đổi vị trí của nó trong bộ nhớ, tức là tham chiếu của nó (còn được gọi là con trỏ của nó).

Cái này sai. Một đối tượng không sở hữu các con trỏ trỏ tới nó, cũng như vị trí bộ nhớ của đối tượng được kiểm soát hoặc bị ảnh hưởng bởi bất kỳ con trỏ nào tới nó.

Vì vậy, hai NSStringđối tượng trong ví dụ của bạn ( @"Bob"@"Mike") hoàn toàn tách biệt với myNamebiến. Họ cũng hoàn toàn tách biệt với nhau. Khi bạn thay đổi myNameđể trỏ đến @"Mike"thay vì chỉ vào @"Bob", bạn không thay đổi các NSStringđối tượng.


Để đầy đủ, tôi sẽ lưu ý rằng các bộ thu gom rác làm cho điều này trở nên phức tạp hơn trong đó việc thay đổi con trỏ có thể ảnh hưởng đến các đối tượng mà chúng trỏ (ed) tới. Tuy nhiên, đây là một chi tiết triển khai không ảnh hưởng đến hành vi có thể quan sát được của mã.


17

Bạn đang lạc lối trong lời nói. Tính không thay đổi có nghĩa là: Miễn là bạn không thay đổi biến, nó sẽ luôn "chứa" cùng một giá trị, không quan trọng bạn làm gì với các biến khác.

Ví dụ mẫu trong C (đơn giản hóa một chút, giả sử một kiến ​​trúc cho phép điều đó):

 char *a = "Hello World";
 char *b = a;
 b[0] = 'Y';

Giờ đây, không còn "chứa" (tức là trỏ vào) chuỗi "Hello World", mà thay vào đó là "Thế giới Yello".

Trong các ngôn ngữ mà chuỗi là bất biến, như Java và (EDIT: safe) C #, bạn không thể làm điều đó. Không đời nào. Điều này có nghĩa là mọi phần của chương trình có thể giữ một tham chiếu đến chuỗi một cách an toàn và tin rằng nội dung của nó không bao giờ thay đổi; nếu không, họ sẽ phải tạo một bản sao chỉ để ở bên an toàn.

Nhưng biến vẫn có thể thay đổi. Bạn có thể để nó trỏ đến một đối tượng khác. Chỉ là đối tượng sẽ không thay đổi sau lưng bạn.


1
Về mặt kỹ thuật bạn có thể sửa đổi nội dung chuỗi trong C #, bạn chỉ cần sử dụng unsafemã.
Aaronaught

1
Aaronaught: Cảm ơn thông tin, bạn đã đúng; cho tất cả những ai cần biết làm thế nào: msdn.microsoft.com/en-us/l
Library / ms228599.aspx

10

Bạn đang nhầm lẫn các biến với các đối tượng. Các biến có thể được sử dụng để lưu trữ các tham chiếu đến các đối tượng, nhưng chúng KHÔNG phải là các đối tượng. Nó là các đối tượng là bất biến, không phải là biến, vì vậy bạn có thể thay đổi biến từ đối tượng này sang đối tượng khác, nhưng bạn không thể thay đổi các thuộc tính của đối tượng nếu nó không thay đổi.

Hãy nghĩ về đối tượng như một người hàng xóm ồn ào, say xỉn. Nếu anh ta hợp lý (có thể thay đổi), bạn có thể gõ cửa anh ta và chuyển anh ta thành một lối sống mà anh ta không gây ra nhiều tiếng ồn. Nhưng nếu anh ấy bất biến, thay đổi duy nhất của bạn là hy vọng rằng người khác sẽ chuyển đến!


3
Tôi đã có một số hàng xóm mà tôi rất thích tắt tiếng.
Dave Nay

Tôi không nghĩ rằng bạn cần ví dụ của bạn trong đoạn thứ hai vì lời giải thích của bạn là đủ tốt. IMO ví dụ có thể chỉ gây nhầm lẫn. Tuy nhiên +1 cho câu trả lời ngắn gọn cho câu hỏi của chap.
Paul McCabe

@DaveNay: Tôi xin lỗi vì chơi chữ lén lút. Không có ý định gì cả.
Kilian Foth

6

Một biến không phải là một đối tượng. Một biến là một tên, dùng để chỉ một đối tượng (hay nói chung là một giá trị).

Ví dụ: "thủ môn" là tên chúng tôi sử dụng để chỉ đối tượng (người) chịu trách nhiệm bảo vệ mục tiêu. Nhưng nếu tôi thay người đó bằng người khác (vì người trước bị thương hoặc không có gì), người mới bây giờ được gọi là "thủ môn".

Câu lệnh gán là thứ làm cho các biến có thể thay đổi (một số ngôn ngữ, chẳng hạn như Haskell không có nó và trên thực tế sử dụng các biến không thay đổi). Nó cho phép bạn xác định lại ý nghĩa của tên và từ đó gán lại giá trị.

Bây giờ các đối tượng có thể là bất biến. Vài ngàn năm trước người ta có thể nghĩ kim cương là bất biến. Cho dù bạn đã làm gì với một viên kim cương, bạn không thể sửa đổi nó. Cho dù bạn gọi nó là waggawooga (tạm dịch là "viên đá sáng bóng lớn nhất của bộ lạc chúng tôi") hoặc ngừng gọi nó theo cách đó (vì bạn đã tìm thấy một cái lớn hơn), viên kim cương vẫn giữ nguyên. Ngược lại, mảnh gỗ bạn từng khắc trong những bức ảnh ngộ nghĩnh với waggawooga của bạn không giống nhau. Nó đã chứng minh sự đột biến. Ngay cả khi nó có cùng tên mọi lúc.

Cả hai biến và giá trị có thể là bất biến (độc lập). Trong trường hợp này, đó là những đối tượng bất biến. Một khi bạn xây dựng một NSString, bạn không thể sửa đổi nó. Bạn có thể gọi nó bằng tên và vượt qua nó, nhưng nó sẽ giữ nguyên. Ngược lại với điều đó, NSMutableStringcó thể được thay đổi sau khi tạo, ví dụ bằng cách gọi setStringphương thức.


Yêu so sánh waggawooga!
Michael K

Để không nhầm lẫn với poster gốc hơn mức cần thiết: mặc dù quan điểm của bạn rằng một biến không phải là một đối tượng là tốt, một biến không thực sự được định nghĩa là "một tên đề cập đến một giá trị". Một biến có thể được đặt tên và đề cập đến một vị trí lưu trữ có chứa một giá trị. Trong nhiều ngôn ngữ lập trình, các biến không cần phải được đặt tên và trong nhiều ngôn ngữ, cùng một biến có thể có nhiều hơn một tên.
Eric Lippert

4

Tôi nghĩ rằng bạn cảm thấy lạc lõng, bởi vì bạn đang trộn lẫn hai khái niệm: chính đối tượng và tên biến bị ràng buộc với đối tượng đó.

Đối tượng bất biến không thể thay đổi. Giai đoạn = Stage. Tuy nhiên, tên biến (ký hiệu) liên kết với một đối tượng bất biến, có thể được sửa đổi để bị ràng buộc với một đối tượng bất biến khác .

Nói cách khác, những gì bạn đã làm trong hai dòng này là:

  1. tạo đối tượng chuỗi bất biến với giá trị "Bob"
  2. biểu tượng liên kết myNamevới đối tượng đó
  3. tạo đối tượng chuỗi bất biến có giá trị "Mike"
  4. biểu tượng liên kết myNamevới đối tượng đó

2

Kiểu của bạn không phải là một NSString, nó là " con trỏ tới một NSString", không phải là bất biến. Tôi không biết gì về mục tiêu C, nhưng tôi đoán trong ví dụ của bạn @"Mike"đang tạo một thể hiện mới NSStringvà gán nó cho con trỏ myName . Vì vậy, bạn đã không thay đổi đối tượng myNameđược trỏ đến, chỉ những gì nó được trỏ đến.


0

Đây là một ví dụ về một đối tượng có thể thay đổi: một mảng chartrong C:

char str[10];

Tôi có thể thay đổi nội dung của nội dungstr trái tim mình (miễn là không quá 10 ký tự). Để nó được nhận dạng là một chuỗi bởi các hàm thư viện chuỗi C, phải có một dấu 0, do đó, nó có thể giữ các chuỗi có độ dài tối đa 9 ký tự:

strcpy(str, "hello"); // str now contains the string "hello\0"
strcpy(str, "world"); // str now contains the string "world\0"

str[0] = 'W';         // str now contains "World\0"

und so weiter .

Tương phản điều này với một đối tượng chuỗi trong Java:

String foo = "Hello";

Trường hợp chuỗi (đoạn bộ nhớ chứa các ký tự 'H', 'e', ​​'l', 'l' và 'o') không thể được sửa đổi; Tôi không thể thay đổi bất kỳ nội dung của chuỗi đó. Khi bạn viết một cái gì đó như

foo = foo + " World";

bạn không nối "Thế giới" vào cuối phiên bản "Xin chào"; bạn đang tạo một thể hiện mới , sao chép "Hello World" vào nó và cập nhật foođể tham chiếu đến thể hiện chuỗi mới đó.

foobản thân nó không chứa thể hiện chuỗi; nó chỉ đề cập đến trường hợp đó (tương tự như một con trỏ trong C hoặc Obj-C), do đó tại sao các kiểu như String được gọi là kiểu tham chiếu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.