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

发布于 2024-09-19 19:58:31 字数 97 浏览 10 评论 0原文

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

可变和不可变对象的内存分配和释放方式有什么区别吗?

I was just curious to know why structs, strings etc are immutable? What is the reason for making them immutable and rest of the objects as mutable. What are the things that are considered to make an object immutable?

Is there any difference on the way how memory is allocated and deallocated for mutable and immutable objects?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

没有伤那来痛 2024-09-26 19:58:31

如果您对这个主题感兴趣,我在 https://ericlippert.com/2011/05/26/atomicity-volatility-and-immutability-are- Different-part-one/

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

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

字符串是不可变的。

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

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

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

  • 第一点的特例:不可变对象更容易实现线程安全。大多数线程安全问题都是由于一个线程上的写入和另一个线程上的读取造成的;不可变对象没有写操作。

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

使结构不可变的原因

使结构不可变的原因有很多。这只是其中之一。

结构体是按值复制的,而不是按引用复制的。很容易意外地将结构视为通过引用复制。例如:

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) 不变。等等。

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

可变对象和不可变对象的内存分配和释放方式有什么区别吗?

不是这样的。正如我之前提到的,不可变值的好处之一是您可以重复使用它们的一部分而无需复制。所以从这个意义上说,内存分配可能非常不同。

If this subject interests you, I have a number of articles about immutable programming at https://ericlippert.com/2011/05/26/atomicity-volatility-and-immutability-are-different-part-one/

I was just curious to know why structs, strings etc are immutable?

Structs and classes are not immutable by default, though it is a best practice to make structs immutable. I like immutable classes too.

Strings are immutable.

What is the reason for making them immutable and rest of the objects as mutable.

Reasons to make all types immutable:

  • It is easier to reason about objects that do not change. If I have a queue with three items in it, I know it is not empty now, it was not empty five minutes ago, it will not be empty in the future. It's immutable! Once I know a fact about it, I can use that fact forever. Facts about immutable objects do not go stale.

  • A special case of the first point: immutable objects are much easier to make threadsafe. Most thread safety problems are due to writes on one thread and reads on another; immutable objects don't have writes.

  • Immutable objects can be taken apart and re-used. For example, if you have an immutable binary tree then you can use its left and right subtrees as subtrees of a different tree without worrying about it. In a mutable structure you typically end up making copies of data to re-use it because you don't want changes to one logical object affecting another. This can save lots of time and memory.

Reasons to make structs immutable

There are lots of reasons to make structs immutable. Here's just one.

Structs are copied by value, not by reference. It is easy to accidentally treat a struct as being copied by reference. For example:

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

Now you want to refactor some of that code into a helper method:

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

WRONG! That should be (ref S s) -- if you don't do that then the mutation will happen on a copy of s. If you don't allow mutations in the first place then all these sorts of problems go away.

Reasons to make strings immutable

Remember my first point about facts about immutable structures staying facts?

Suppose string were mutable:

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

What if the hostile caller mutates filename after the security check and before the file is opened? The code just opened a file that they might not have permission to!

Again, mutable data is hard to reason about. You want the fact "this caller is authorized to see the file described by this string" to be true forever, not until a mutation happens. With mutable strings, to write secure code we'd constantly have to be making copies of data that we know do not change.

What are the things that are considered to make an object immutable?

Does the type logically represent something that is an "eternal" value? The number 12 is the number 12; it doesn't change. Integers should be immutable. The point (10, 30) is the point (10, 30); it doesn't change. Points should be immutable. The string "abc" is the string "abc"; it doesn't change. Strings should be immutable. The list (10, 20, 30) doesn't change. And so on.

Sometimes the type represents things that do change. Mary Smith's last name is Smith, but tomorrow she might be Mary Jones. Or Miss Smith today might be Doctor Smith tomorrow. The alien has fifty health points now but has ten after being hit by the laser beam. Some things are best represented as mutations.

Is there any difference on the way how memory is allocated and deallocated for mutable and immutable objects?

Not as such. As I mentioned before though, one of the nice things about immutable values is that something you can re-use parts of them without making copies. So in that sense, memory allocation can be very different.

℡Ms空城旧梦 2024-09-26 19:58:31

结构不一定是不可变的,但可变结构是邪恶的。

创建可变结构可能会导致应用程序中出现各种奇怪的行为,因此,它们被认为是一个非常糟糕的主意(因为它们看起来像引用类型,但实际上是值类型,并且每当您传递时都会被复制)他们周围)。

另一方面,字符串是不可变的。这使得它们本质上是线程安全的,并且允许通过字符串驻留进行优化。如果您需要即时构建复杂的字符串,可以使用StringBuilder

Structs are not necessarily immutable, but mutable structs are evil.

Creating mutable structs can lead to all kinds of strange behavior in your application and, therefore, they are considered a very bad idea (stemming from the fact that they look like a reference type but are actually a value type and will be copied whenever you pass them around).

Strings, on the other hand, are immutable. This makes them inherently thread-safe as well as allowing for optimizations via string interning. If you need to construct a complicated string on the fly, you can use StringBuilder.

情痴 2024-09-26 19:58:31

结构类型不是不可变的。是的,字符串是。使您自己的类型不可变很容易,只需不提供默认构造函数,将所有字段设为私有,并且不定义更改字段值的方法或属性。让一个应该改变对象的方法返回一个新对象。有一个内存管理的角度,你往往会创建大量的副本和垃圾。

A struct type is not immutable. Yes, strings are. Making your own type immutable is easy, simply don't provide a default constructor, make all fields private and define no methods or properties that change a field value. Have a method that should mutate the object return a new object instead. There is a memory management angle, you tend to create a lot of copies and garbage.

尴尬癌患者 2024-09-26 19:58:31

可变性和不可变性的概念应用于结构和类时具有不同的含义。可变类的一个关键方面(通常是关键弱点)是 Foo 是否有一个 List 类型的字段 Bar,其中包含对包含 (1,2,3) 的列表的引用,引用同一列表的其他代码可以修改它,这样 Bar 保存对包含 (4,5, 6),即使其他代码无法访问 Bar。相比之下,如果 Foo 具有 System.Drawing.Point 类型的字段 Biz,则任何内容都可以修改 任何方面的唯一方法Biz拥有该字段的写入权限

结构体的字段(公共和私有)可以由任何可以改变存储该结构体的存储位置的代码来改变,并且不能由任何不能改变其存储位置的代码来改变。如果封装在结构体中的所有信息都保存在其字段中,则这样的结构体可以有效地将不可变类型的控制与可变类型的便利性结合起来,除非该结构体的编码方式消除了这种便利性(不幸的是,一些微软程序员推荐了这个习惯)。

结构的“问题”是,当在只读上下文(或不可变位置)中的结构上调用方法(包括属性实现)时,系统会复制该结构,在临时副本上执行该方法,然后默默地执行该方法。丢弃结果。这种行为导致程序员提出了一个不幸的想法,即避免变异方法出现问题的方法是让许多结构不允许分段更新,而通过简单地用公开字段替换属性可以更好地避免问题。 >。

顺便说一句,有些人抱怨当类属性返回一个方便可变的结构时,对该结构的更改不会影响它所来自的类。我认为这是一件好事——返回的项是一个结构这一事实使得行为变得清晰(特别是如果它是一个暴露字段结构)。将使用 Drawing.Matrix 上的假设结构和属性的片段与使用 Microsoft 实现的该类上的实际属性的片段进行比较:

// Hypothetical struct
public struct {
  public float xx,xy,yx,yy,dx,dy;
} Transform2d;

// Hypothetical property of "System.Drawing.Drawing2d.Matrix"
public Transform2d Transform {get;}

// Actual property of "System.Drawing.Drawing2d.Matrix"
public float[] Elements { get; }

// Code using hypothetical struct
Transform2d myTransform = myMatrix.Transform;
myTransform.dx += 20;
... other code using myTransform

// Code using actual Microsoft property
float[] myArray = myMatrix.Elements;
myArray[4] += 20;
... other code using myArray

查看实际的 Microsoft 属性,是否有任何方法可以判断是否写入到myArray[4]会影响myMatrix吗?即使查看页面 http://msdn。 microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx 有什么方法可以告诉吗?如果该属性是使用基于结构的等效项编写的,则不会产生混淆;返回该结构的属性将返回最多也不小于六个数字的当前值。更改 myTransform.dx 只不过是写入一个不附加任何其他内容的浮点变量。任何不喜欢更改 myTransform.dx 不会影响 myMatrix 的人都应该同样恼火,因为编写 myArray[4] 不会影响也不影响 myMatrix,除了 myMatrixmyTransform 的独立性是显而易见的,而 myMatrix 的独立性而 myArray 则不然。

The concepts of mutability and immutability have different meanings when applied to structs and classes. A key aspect (oftentimes, the key weakness) of mutable classes is if Foo has a field Bar of type List<Integer>, which holds a reference to a list containing (1,2,3), other code which has a reference to that same list could modify it, such that Bar holds a reference to a list containing (4,5,6), even if that other code has no access whatsoever to Bar. By contrast, if Foo had a field Biz of type System.Drawing.Point, the only way anything could modify any aspect of Biz would be to have write access to that field.

The fields (public and private) of a struct can be mutated by any code which can mutate the storage location in which the struct is stored, and cannot be mutated by any code which cannot mutate the storage location where it is stored. If all of the information encapsulated within a struct is held in its fields, such a struct can effectively combine the control of an immutable type with the convenience of a mutable type, unless the struct is coded in such a way as to remove such convenience (a habit which, unfortunately, some Microsoft programmers recommend).

The "problem" with structs is that when a method (including a property implementation) is invoked on a struct in a read-only context (or immutable location), the system copies the struct, performs the method on the temporary copy, and silently discards the result. This behavior has led programmers to put forth the unfortunate notion that the way to avoid problems with mutating methods is to have many structs disallow piecewise updates, when the problems could have been better avoided by simply replacing properties with exposed fields.

Incidentally, some people complain that when a class property returns a conveniently-mutable struct, changes to the struct don't affect the class from which it came. I would posit that's a good thing--the fact that the returned item is a struct makes the behavior clear (especially if it's an exposed-field struct). Compare a snippet using a hypothetical struct and property on Drawing.Matrix with one using an actual property on that class as implemented by Microsoft:

// Hypothetical struct
public struct {
  public float xx,xy,yx,yy,dx,dy;
} Transform2d;

// Hypothetical property of "System.Drawing.Drawing2d.Matrix"
public Transform2d Transform {get;}

// Actual property of "System.Drawing.Drawing2d.Matrix"
public float[] Elements { get; }

// Code using hypothetical struct
Transform2d myTransform = myMatrix.Transform;
myTransform.dx += 20;
... other code using myTransform

// Code using actual Microsoft property
float[] myArray = myMatrix.Elements;
myArray[4] += 20;
... other code using myArray

Looking at the actual Microsoft property, is there any way to tell whether the write to myArray[4] will affect myMatrix? Even looking at the page http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx is there any way to tell? If the property had been written using the struct-based equivalent, there would be no confusion; the property that returns the struct would return nothing more nor less than the present value of six numbers. Changing myTransform.dx would be nothing more nor less than a write to a floating-point variable which was unattached to anything else. Anyone who doesn't like the fact that changing myTransform.dx doesn't affect myMatrix should be equally annoyed that writing myArray[4] doesn't affect myMatrix either, except that the independence of myMatrix and myTransform is apparent, while the independence of myMatrix and myArray is not.

最舍不得你 2024-09-26 19:58:31

结构体可以是可变的,但这是一个坏主意,因为它们具有复制语义。如果您对结构进行更改,您实际上可能正在修改一个副本。准确跟踪已更改的内容非常棘手。

可变结构会产生错误。

Structs can be mutable, but it's a bad idea because they have copy-semantics. If you make a change to a struct, you might actually be modifying a copy. Keeping track of exactly what has been changed is very tricky.

Mutable structs breed mistakes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文