繁体   English   中英

包含引用类型的Struct

[英]Struct containing reference types

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);
}

输出是:

127.0.0.1:8080
255.255.255.255:9090

为什么address1IP (字符串,即参考类型)不会改变? 如果我用IPAddress替换string来表示MyIPEndPoint的IP,则会出现相同的行为:虽然IPAddress是一个类(即引用类型),但它不像行为类型那样。 为什么?

实际上,如果我使用新的简单类MyIP包装代表IP的string ,则行为会发生变化。

public class MyIP
{
    public string IpAsString;

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

当然,您还应该按以下方式调整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;
    }
}

最后在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);

现在的输出是:

255.255.255.255:8080
255.255.255.255:9090

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

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

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

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

MyIPEndPoint address2 = address1;      

现在您带一台复印机并在“地址一”抽屉里复印一份纸张,并将副本放入“地址二抽屉”中。

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

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

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

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

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

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

MyIPEndPoint address2 = address1;  

您在“地址一”中复印纸张,并将副本放入“地址二”。

address2.IP.IpAsString = "255.255.255.255"; 

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

address2.Port = 9090;  

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

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

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

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

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

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

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

在这种情况下, 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)

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


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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM