包含引用类型的Struct

enz*_*m83 25 .net c#

struct是一个值类型,所以如果我将一个struct分配给另一个struct,它的字段将被复制到第二个struct中.但是,如果结构的某些字段是引用类型会发生什么?

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);
}
Run Code Online (Sandbox Code Playgroud)

输出是:

127.0.0.1:8080
255.255.255.255:9090
Run Code Online (Sandbox Code Playgroud)

为什么IP(一个字符串,即参考类型)address1不会改变?如果我更换会发生同样的行为stringIPAddress代表IP内MyIPEndPoint虽然:IPAddress是一类(即引用类型),它不表现为引用类型.为什么?

实际上,如果我string使用一个新的简单类来包装代表IP 的那个MyIP,那么行为就会改变.

public class MyIP
{
    public string IpAsString;

    public MyIP(string s)
    {
        IpAsString = s;
    }
    public override string ToString()
    {
        return IpAsString;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您还应该MyIPEndPoint按以下方式调整结构:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后在Main我改变了一个声明:

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);
Run Code Online (Sandbox Code Playgroud)

现在的输出是:

255.255.255.255:8080
255.255.255.255:9090
Run Code Online (Sandbox Code Playgroud)

我在第一种情况下期待这个输出.为什么在第一种情况下引用的行为不符合预期

Eri*_*ert 28

考虑你的第一个例子.你有两个抽屉,标有"地址一"和"地址二".两个抽屉都是空的.

MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080);     
Run Code Online (Sandbox Code Playgroud)

现在你得到一张纸,然后在那张纸上写下"127.0.0.1,8080",并将它放在抽屉1中.

MyIPEndPoint address2 = address1;      
Run Code Online (Sandbox Code Playgroud)

现在您带一台复印机并在"地址一"抽屉里复印一份纸张,然后将副本放在"地址二抽屉"中.

address2.IP = "255.255.255.255";     
address2.Port = 9090; 
Run Code Online (Sandbox Code Playgroud)

现在,您将纸张放在地址两个抽屉中,然后划掉那里的内容,并将其替换为新文本.

抽屉里的纸张没有改变.它仍然和以前一样.

现在考虑你的第二个例子.现在你有两个空抽屉,和以前一样,还有一本空白纸.

MyIPEndPoint address1 = new MyIPEndPoint("127.0.0.1", 8080); 
Run Code Online (Sandbox Code Playgroud)

你随机挑选一本书的页面并将其命名为"REFERENCE ONE".在该页面上,您编写"127.0.0.1".你拿一张松散的纸,在那张纸上写上"REFERENCE ONE,8080"并把它贴在标有"地址一"的抽屉里.

MyIPEndPoint address2 = address1;  
Run Code Online (Sandbox Code Playgroud)

您在"地址一"中复印纸张,并将副本放入"地址二".

address2.IP.IpAsString = "255.255.255.255"; 
Run Code Online (Sandbox Code Playgroud)

你打开抽屉"地址二",看到它说"REFERENCE ONE".你仔细阅读这本书,直到找到一个名为"REFERENCE ONE"的页面.你划掉那里的东西并用新文本替换它.

address2.Port = 9090;  
Run Code Online (Sandbox Code Playgroud)

你打开抽屉"地址二"并划出"8080"并用"9090"替换它.你将REFERENCE ONE留在原处.

现在当你完成后,抽屉"地址一"包含"参考一,8080",抽屉"地址二"包含"参考一,9090",书中有一个页面,上面写着"参考一:255.255.255.255".

现在您了解参考和价值类型之间的区别吗?


Ant*_*ram 19

您已正确理解,对于结构,address1和address2不是同一个对象.这些值被复制了.但是,对于该领域,这是一个简单的重新分配案例.它与字符串是引用类型或任何特殊规则或任何不变性建议这一事实无关.您只需使用其他值重新分配属性或字段.

someStruct.SomeString = "A";
anotherStruct = someStruct;
anotherStruct.SomeString = "B"; // would never affect someStruct
Run Code Online (Sandbox Code Playgroud)

您已在此示例中覆盖了引用.事实上,在短暂的时刻,两个结构的字段包含相同的参考并不重要.在第二个例子中,你做了一些非常不同的事情.

someStruct.IP.SomeString = "A";
anotherStruct = someStruct;
anotherStruct.IP.SomeString = "B"; 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,IP的值没有改变.部分IP的状态已经改变.每个struct的字段仍然引用相同的IP.

简单来说

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)
Run Code Online (Sandbox Code Playgroud)

变量和字段包含值.对于类,这些值是对象的引用.两个变量或字段可以包含相同的引用,但这并不意味着这些变量本身是链接的.无论如何它们都没有连接,它们只是具有共同的价值.替换一个变量或字段的值时,另一个变量不受影响.


不是针对特定主题,但值得注意的是,许多人认为可变结构是邪恶的.其他人并不完全持相同观点,或者至少不那么虔诚.(然而,这是值得注意的是,地址是一个类,那么地址1和地址2将保持相同的值(所述参考地址对象),并且修改的状态地址1的将是经由地址2可见,只要既不地址1或地址2本身被重新分配.)

如果这是您的代码的实际表示,那么值得对可变结构进行一些研究,这样您至少可以完全理解您可能遇到的各种陷阱.