一尘不染

为什么C#结构是不可变的?

c#

我只是想知道为什么结构,字符串等是不可变的?是什么原因使它们不可变,而其余对象却可变。什么被认为使对象不可变?

为可变和不可变对象分配和释放内存的方式有何不同?


阅读 238

收藏
2020-05-19

共1个答案

一尘不染

如果您对此主题感兴趣,我在http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/上有很多关于不可变编程的文章。

我只是想知道为什么结构,字符串等是不可变的?

默认情况下,结构和类不是不变的,尽管使结构不变是最佳做法。我也喜欢不可变的类。

字符串是不可变的。

是什么原因使它们不可变,而其余对象却可变。

使所有类型不可变的原因:

  • 对于不变的对象,更容易推断。如果我有一个队列中包含三个项目,那么我知道它现在不为空,五分钟前它也不为空,将来也不会为空。这是一成不变的!一旦知道了一个事实,便可以永远使用该事实。关于不可变对象的事实不会过时。

  • 第一点的特殊情况:不可变对象使线程安全变得容易得多。大多数线程安全问题是由于在一个线程上进行写入而在另一个线程上进行读取所致。不可变的对象没有写操作。

  • 不变的对象可以拆开并重新使用。例如,如果您有一棵不可变的二叉树,那么您可以将其左和右子树用作 不同 树的子树,而不必担心它。在可变结构中,您通常最终会制作数据副本以重复使用,因为您不希望更改一个逻辑对象而影响另一个逻辑对象。这样可以节省 大量 时间和内存。

使结构不变的原因

使结构不可变的原因很多。这只是一个。

结构是按值复制的,而不是按引用复制的。偶然将结构当作引用复制很容易。例如:

void M()
{
    S s = whatever;
    ... lots of code ...
    s.Mutate();
    ... lots more code ...
    Console.WriteLine(s.Foo);
    ...
}

现在,您想将其中一些代码重构为一个辅助方法:

void Helper(S s)
{
    ... lots of code ...
    s.Mutate();
    ... lots more code ...
}

错误!那应该是(ref S s)-如果不这样做,那么突变将发生在s 的 副本 上。如果您首先不允许发生突变,那么所有这些类型的问题都会消失。

使字符串不可变的原因

还记得我关于不变结构的事实留下的第一点吗?

假设字符串是可变的:

public static File OpenFile(string filename)
{
    if (!HasPermission(filename)) throw new SecurityException();
    return InternalOpenFile(filename);
}

如果敌对呼叫者变异什么名 的安全检查和 之前 的文件被打开?代码刚刚打开了一个他们可能没有权限的文件!

同样,可变数据很难推理。您希望“此调用者被授权查看此字符串描述的文件”这一事实 永远 是对的,而不是 直到发生突变为止
。使用可变的字符串,要编写安全的代码,我们必须不断地制作我们知道不会更改的数据的副本。

什么被认为使对象不可变?

类型是否在逻辑上表示“永恒”值?数字12是数字12;它没有改变。整数应该是不变的。点(10,30)是点(10,30); 它没有改变。点应该是不变的。字符串“
abc”是字符串“ abc”;它没有改变。字符串应该是不可变的。列表(10、20、30)不变。等等。

有时,类型表示确实发生变化的事物。玛丽·史密斯的姓氏是史密斯,但明天她可能是玛丽·琼斯。或者今天的史密斯小姐明天可能是史密斯医生。外星人现在有五十个健康点,但是被激光束击中后有十个健康点。有些事情最好用突变来表示。

为可变和不可变对象分配和释放内存的方式有何不同?

并非如此。如前所述,关于不变值的一件好事是,您无需复制就可以重用其中的一部分。因此,从这个意义上讲,内存分配可能非常不同。

2020-05-19