2012-02-08 38 views
23

Cấu trúc là một kiểu giá trị, vì vậy nếu tôi gán một cấu trúc cho cấu trúc khác, các trường của nó sẽ được sao chép trong cấu trúc thứ hai. Nhưng, điều gì sẽ xảy ra nếu một số trường của cấu trúc là một kiểu tham chiếu?Cấu trúc chứa các kiểu tham chiếu

public struct MyIPEndPoint 
{ 
    public String IP; 
    public UInt16 Port; 

    public MyIPEndPoint(String ipAddress, UInt16 portNumber) 
    { 
     IP = ipAddress; 
     Port = portNumber; 
    } 

    public override string ToString() 
    { 
     return IP+":"+Port; 
    } 
} 

... 

static int Main(string[] args) 
{ 
    MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080); 
    MyIPEndPoint address2 = address1; 

    address2.IP = "255.255.255.255"; 
    address2.Port = 9090; 

    Console.WriteLine(address1); 
    Console.WriteLine(address2); 
} 

Đầu ra là:

127.0.0.1:8080 
255.255.255.255:9090 

Tại sao IP (một chuỗi, đó là một loại tài liệu tham khảo) của address1 không thay đổi? Hành vi tương tự xảy ra nếu tôi thay thế string bằng IPAddress để đại diện cho IP trong phạm vi MyIPEndPoint: mặc dù IPAddress là một lớp (là loại tham chiếu), nó không hoạt động như một loại tham chiếu. Tại sao?

Thật vậy, nếu tôi quấn string đại diện cho IP bằng cách sử dụng một lớp đơn giản mới MyIP, thay đổi hành vi.

public class MyIP 
{ 
    public string IpAsString; 

    public MyIP(string s) 
    { 
     IpAsString = s; 
    } 
    public override string ToString() 
    { 
     return IpAsString; 
    } 
} 

Tất nhiên bạn cũng nên điều chỉnh MyIPEndPoint struct theo cách sau:

public struct MyIPEndPoint 
{ 
    public MyIP IP; // modification 
    public UInt16 Port; 

    public MyIPEndPoint(String ipAddress, UInt16 portNumber) 
    { 
     IP = new MyIP(ipAddress); // modification 
     Port = portNumber; 
    } 

    public override string ToString() 
    { 
     return IP+":"+Port; 
    } 
} 

Cuối cùng trong Main tôi đã thay đổi chỉ một tuyên bố:

MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080); 
MyIPEndPoint address2 = address1; 

address2.IP.IpAsString = "255.255.255.255"; // modification 
address2.Port = 9090; 

Console.WriteLine(address1); 
Console.WriteLine(address2); 

Bây giờ đầu ra là:

255.255.255.255:8080 
255.255.255.255:9090 

Tôi đã mong đợi kết quả này trong trường hợp đầu tiên. Tại sao trong trường hợp đầu tiên tham chiếu không hoạt động như mong đợi?

Trả lời

17

Bạn đã hiểu chính xác rằng với cấu trúc, địa chỉ 1 và địa chỉ 2 không phải là cùng một đối tượng. Các giá trị đã được sao chép. Tuy nhiên, đối với lĩnh vực này, đây là một trường hợp chuyển nhượng đơn giản. Nó không có gì để làm với thực tế là chuỗi là một loại tài liệu tham khảo hoặc bất kỳ quy tắc đặc biệt hoặc bất kỳ đề nghị bất biến. Bạn vừa gán lại một thuộc tính hoặc trường với một giá trị khác.

someStruct.SomeString = "A"; 
anotherStruct = someStruct; 
anotherStruct.SomeString = "B"; // would never affect someStruct 

Bạn đã ghi đè tham chiếu trong ví dụ này. Thực tế là trong một thời gian ngắn, cả hai lĩnh vực của các cấu trúc có cùng tham chiếu đều không quan trọng. Trong ví dụ thứ hai của bạn, bạn đã làm một cái gì đó rất khác nhau.

someStruct.IP.SomeString = "A"; 
anotherStruct = someStruct; 
anotherStruct.IP.SomeString = "B"; 

Trong trường hợp này, giá trị của IP vẫn không thay đổi. Một phần của Trạng thái IP đã thay đổi. Mỗi trường của struct vẫn tham chiếu cùng một IP.

Đặt trong điều kiện đơn giản

var foo = new Foo(); // Foo is class 
var other = foo; 
// other and foo contain same value, a reference to an object of type Foo 
other = new Foo(); // was foo modified? no! 

int x = 1; 
int y = x; 
y = 2; // was x modified? of course not. 

string s = "S"; 
string t = s; 
t = "T"; // is s "T"? (again, no) 

biến và các lĩnh vực giữ giá trị. Đối với các lớp, các giá trị đó là các tham chiếu đến các đối tượng. Hai biến hoặc trường có thể giữ cùng một tham chiếu, nhưng điều đó không có nghĩa là các biến đó được liên kết. Họ không được kết nối trong anyway, họ chỉ đơn giản là giữ một giá trị chung. Khi bạn thay thế một giá trị cho một biến hoặc trường, biến khác không bị ảnh hưởng.


Không có chủ đề cụ thể, nhưng cần lưu ý rằng các cấu trúc có thể thay đổi được nhiều người xem là điều ác. Những người khác không hoàn toàn giữ quan điểm tương tự, hoặc ít nhất là không tôn giáo. (Tuy nhiên, đó là cũng là đáng chú ý rằng Địa chỉ là một lớp, sau đó address1 và address2 sẽ giữ cùng một giá trị (tham chiếu đến đối tượng Địa chỉ) và sửa đổi thành trạng thái của địa chỉ1 sẽ hiển thị qua address2 miễn là không phải address1 hoặc address2 là tự bố trí.)

Nếu đây là một đại diện thực tế của mã của bạn, nó sẽ có giá trị thực hiện một số nghiên cứu về mutable structs vì vậy bạn ít nhất có một sự hiểu biết đầy đủ về những cạm bẫy khác nhau mà bạn có thể gặp phải.

+0

Tôi hiểu lý do: nói cách khác, nếu IPAddress có trường công khai, tôi có thể tạo lại kết quả tương tự. – enzom83

+1

Nếu bạn có thể sửa đổi trạng thái của đối tượng được tham chiếu bởi biến chuỗi (hoặc trường) mà không thực sự ghi đè tham chiếu, bạn có thể quan sát các thay đổi trong cả hai cấu trúc. Nhưng bạn không sửa đổi trạng thái (và bạn không thể, ít nhất là không có trong mã an toàn), bạn ghi đè hoàn toàn tham chiếu thông qua chuyển nhượng lại. Trong lần thứ hai, bạn đã sửa đổi trạng thái của đối tượng đã được tham chiếu bởi cả hai cá thể struct. Giá trị của IP, * tham chiếu đến đối tượng *, không thay đổi, vì vậy bạn đã quan sát kết quả trong cả hai. –

+1

Tôi không chắc chắn tuyên bố của bạn là đúng, "Nó không có gì để làm với thực tế là chuỗi là một loại tài liệu tham khảo hoặc bất kỳ quy tắc đặc biệt hoặc bất kỳ đề nghị bất biến." Nếu một chuỗi là một kiểu tham chiếu thông thường, thì việc thay đổi giá trị thông qua một tham chiếu sẽ thay đổi nó cho tất cả các tham chiếu. Nhưng chuỗi là một kiểu tham chiếu không thay đổi, có nghĩa là nó hoạt động giống như một kiểu giá trị. http://stackoverflow.com/questions/636932/in-c-why-is-string-a-reference-type-that-behaves-like-a-value-type. Tuyên bố của bạn là đúng nếu bạn giả sử "=" luôn có nghĩa là chỉ định tham chiếu, nhưng với một số thì nó có nghĩa là giá trị sao chép. – BlueMonkMN

27

Hãy xem ví dụ đầu tiên của bạn. Bạn có hai ngăn kéo, được dán nhãn "địa chỉ một" và "địa chỉ hai". Cả hai ngăn kéo đều trống.

MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080);  

Bây giờ bạn nhận được một mảnh giấy, và bạn viết "127.0.0.1, 8080" trên giấy đó, và đặt nó trong ngăn kéo 1.

MyIPEndPoint address2 = address1;  

Bây giờ bạn có một máy photocopy và làm một bản sao của giấy trong ngăn kéo "địa chỉ một", và đặt bản sao trong "địa chỉ hai ngăn kéo".

address2.IP = "255.255.255.255";  
address2.Port = 9090; 

Bây giờ bạn lấy giấy trong ngăn kéo địa chỉ hai và làm xước những gì đã có và thay thế bằng văn bản mới.

Giấy trong ngăn kéo không thay đổi. Nó vẫn giống như trước đây.

Bây giờ, hãy xem xét ví dụ thứ hai của bạn. Bây giờ bạn có hai ngăn kéo trống, giống như trước đây, và một cuốn sách giấy trắng.

MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080); 

Bạn chọn một trang của cuốn sách một cách ngẫu nhiên và đặt tên là "REFERENCE ONE". Trên trang đó bạn viết "127.0.0.1". Bạn lấy một mảnh giấy rời và viết "REFERENCE ONE, 8080" trên giấy đó và dán nó vào ngăn kéo có nhãn "địa chỉ một".

MyIPEndPoint address2 = address1; 

Bạn tạo bản sao giấy trong "địa chỉ một" và đặt bản sao vào "địa chỉ hai".

address2.IP.IpAsString = "255.255.255.255"; 

Bạn mở ngăn kéo "địa chỉ hai" và thấy rằng nó nói "REFERENCE ONE". Bạn xem qua cuốn sách cho đến khi bạn tìm thấy một trang có nội dung "REFERENCE ONE". Bạn cào ra những gì có và thay thế nó bằng văn bản mới.

address2.Port = 9090; 

Bạn mở ngăn kéo "địa chỉ hai" và làm xước "8080" và thay thế bằng "9090". Bạn rời khỏi REFERENCE ONE ở đâu.

Và bây giờ khi bạn làm xong, ngăn kéo "địa chỉ một" chứa "REFERENCE ONE, 8080", ngăn kéo "địa chỉ hai" chứa "REFERENCE ONE, 9090" và sách có trang "REFERENCE ONE: 255.255 .255.255 ".

Bây giờ bạn có hiểu sự khác biệt giữa các loại tham chiếu và giá trị không?

+0

Có, ví dụ này đã giúp tôi hiểu rõ hơn sự khác biệt giữa các loại tham chiếu và giá trị! – enzom83

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