A struct is a value type, so if I assign a struct to another struct, its fields will be copied in the second struct. But, what happens if some fields of the struct are a reference type?
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);
}
The output is:
127.0.0.1:8080
255.255.255.255:9090
Why the IP (a string, that is a reference type) of address1 does not change?
The same behavior occurs if I replace string with IPAddress to represent the IP within MyIPEndPoint: although IPAddress is a class (that is a reference type), it does not behave as a reference type. Why?
Indeed, if I wrap the string which represent the IP using a new simple class MyIP, the behavior changes.
public class MyIP
{
public string IpAsString;
public MyIP(string s)
{
IpAsString = s;
}
public override string ToString()
{
return IpAsString;
}
}
Of course you should also adjust the MyIPEndPoint struct in the following way:
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;
}
}
Finally in the Main I changed only a statement:
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);
Now the output is:
255.255.255.255:8080
255.255.255.255:9090
I was expecting this output in the first case.
Why in the first case does the reference not behave as expected?
You have correctly understood that with structs, address1 and address2 are not the same object. The values were copied. However, for the field, this is a simple case of reassignment. It has nothing to do with the fact that string is a reference type or any special rules or any suggestion of immutability. You have simply reassigned a property or field with another value.
You have overwritten the reference in this example. The fact that for a brief moment, both structs’ fields contained the same reference is of no importance. In your second example, you did something very different.
In this case, the value of IP has not changed. Part of IP’s state has changed. Each struct’s field is still referencing the same IP.
Put in simpler terms
Variables and fields hold values. For classes, those values are references to objects. Two variables or fields can hold the same reference, but that does not mean those variables themselves are linked. They are not connected in anyway, they simply hold a common value. When you replace a value for one variable or field, the other variable is not affected.
Not on the specific topic, but it is worth noting that mutable structs are viewed by many as evil. Others don’t quite hold the same view, or at least not as religiously. (However, it is also worth noting that had Address been a class, then address1 and address2 would hold the same value (the reference to the Address object), and modification to the state of address1 would be visible via address2 as long as neither address1 or address2 are themselves reassigned.)
If this is an actual representation of your code, it would be worth doing some research on mutable structs so you at least have a full understanding of various pitfalls you may encounter.