.NET 不可变对象使用最佳实践?我应该尽可能多地使用它们吗?

发布于 2024-08-26 23:31:01 字数 405 浏览 8 评论 0原文

假设我有一个简单的对象,就像

class Something
{
   public int SomeInt { get; set; }
}

我读过的那样,使用不可变对象更快,并且是使用业务对象的更好方法?如果是这样,我是否应该努力使我的所有对象都像这样:

class ImmutableSomething
{
   public int SomeInt { get { return m_someInt; } }
   private int m_someInt = 0;

   public void ChangeSomeInt(int newValue)
   {
       m_someInt = newvalue;
   }
}

你觉得怎么样?

Say I have a simple object such as

class Something
{
   public int SomeInt { get; set; }
}

I have read that using immutable objects are faster and a better means of using business objects? If this is so, should i strive to make all my objects as such:

class ImmutableSomething
{
   public int SomeInt { get { return m_someInt; } }
   private int m_someInt = 0;

   public void ChangeSomeInt(int newValue)
   {
       m_someInt = newvalue;
   }
}

What do you reckon?

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

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

发布评论

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

评论(5

疧_╮線 2024-09-02 23:31:01

你所描绘的并不是一个一成不变的物体;简单地将 set 代码移动到专用的 setter 方法中并不会使对象变得不可变。根据定义,不可变对象无法更改,因此您可以更改对象的任何属性或字段的值,这意味着它不是不可变的。

就“更快”或“更好”而言,不可变对象本质上并不比可变对象更快或“更好”;除了您无法更改任何值之外,它们没有任何特别之处。

What you depict is not an immutable object; simply moving the set code into a dedicated setter method doesn't make an object immutable. An immutable object, by definition, can't change, so the fact that you can alter the value of any of the object's properties or fields means that it isn't immutable.

In terms of "faster" or "better", immutable objects are not intrinsically faster or "better" than mutable objects; there isn't anything special about them, other than the fact that you can't change any values.

不爱素颜 2024-09-02 23:31:01

正如其他人所说,您发布的内容并不是一成不变的。这就是不可变对象的样子。 readonly 关键字意味着唯一可以设置属性支持字段的位置是构造函数中。本质上,对象被构造之后就永远是这样了。

public class ImmutableSomething
{
    private readonly int _someInt;
    public int SomeInt
    {
        get
        {
            return _someInt;
        }
    }

    public ImmutableSomething(int i)
    {
        _someInt = i;
    }

    public ImmutableSomething Add(int i){
        return new ImmutableSomething(_someInt + i);
    }
}

这在函数式编程中是一件大事,因为你不再谈论对象,而是谈论值。值永远不会改变,因此您知道当您将它们传递给函数或方法时,您的原始值将永远不会更改或更新。对于可变对象,你无法做出这样的保证。

使用不可变对象构建的代码可以轻松并行化,因为没有可写的共享状态。如果其他某个线程获取了您的值并想要对其执行某些操作,则它不能。以任何方式“更改”它都会产生一个新对象,并在内存中为该对象提供一个全新的位置。

因此,一旦你处理了值,你就可以做一些特殊的事情,比如实习,这就是 .NET 对字符串所做的事情。因为“Hello World”是一个值并且永远不会改变,所以对“Hello World”的一次引用与任何其他引用一样好,因此您不必将“Hello World”放在内存中的一百个插槽中,而是将其放在一个插槽中,然后将所有对“Hello World”的引用设置为指向该插槽。因此,您在内存方面取得了巨大的胜利,但您付出了性能损失,因为每当您创建新字符串时,您都必须检查实习池以确保它尚不存在。

As others have said what you've posted isn't immutable. This is what an immutable object looks like. The readonly keyword means that the only place that the backing field for the property can be set is in the constructor. Essentially, after the object is constructed that's it forever.

public class ImmutableSomething
{
    private readonly int _someInt;
    public int SomeInt
    {
        get
        {
            return _someInt;
        }
    }

    public ImmutableSomething(int i)
    {
        _someInt = i;
    }

    public ImmutableSomething Add(int i){
        return new ImmutableSomething(_someInt + i);
    }
}

This is a big deal in functional programming because instead of talking about objects you get to talk about Values. Values never change, so you know that when you pass them to a function or method your original value will never be changed or updated. With mutable objects you can't make that guarantee.

Code built with immutable objects can be easily parallelized because there is no writable shared state. If some other thread gets your Value and wants to do something to it, it can't. "Changing" it in any way produces a new object with a brand new spot in memory just for that object.

So once you're dealing with values you can do some special things like interning which is what .NET does with strings. Because "Hello World" is a value and will never change, one reference to "Hello World" is just as good as any other, so instead of having "Hello World" in a hundred slots in memory, you have it in one slot and set all the references to "Hello World" to point to that one slot. So you get a big win on the memory side but you pay a performance penalty because whenever you create a new string you have to check the intern pool to make sure it doesn't already exist.

榕城若虚 2024-09-02 23:31:01

深度不可变对象的主要优点是很容易获取其属性的“快照”——只需复制对对象的引用即可。无论对象有多大或多复杂,只需复制一个引用就可以“快照”整个对象。

相比之下,如果想要获取可变对象属性的快照,则必须复制所有属性。如果这些属性中的任何一个本身是可变对象,则也有必要复制所有这些属性。在某些情况下,制作可变对象状态的可用副本可能非常复杂甚至不可能,因为对象的状态可能与单例的状态交织在一起。

尽管不可变对象比可变对象更容易“快照”,但给定一个不可变对象,有时很难生成一个与第一个对象类似的实例(除了一些细微的更改)。在这方面,可变对象有时更容易使用。有时,将数据从不可变对象复制到可变对象,更改它,然后生成一个新的不可变对象来保存更改的数据可能很有用。不幸的是,没有任何通用方法可以使用类自动执行此类转换。然而,还有一个替代方案。

具有公开字段(全部为值原语或不可变类引用)的结构可以提供一种非常方便的方法,将信息保存在不可变对象中,将其复制到可变形式,修改它,然后创建一个新的不可变对象。或者,可以通过仅复制结构本身来轻松复制结构中的数据。复制包含超过一两个 int 大小字段的结构比复制不可变对象引用要昂贵一些,但复制结构并更改它通常比创建新的更改的不可变对象要便宜得多目的。值得注意的是,由于 .net 语言处理结构的方式存在一些怪癖,有些人认为可变结构是邪恶的。我建议,一般来说,结构最好简单地公开其字段,并避免使用任何会改变 this 的方法(构造函数除外)。这将避免与可变结构相关的大多数怪癖,并且通常会比使用不可变类、可变类或所谓的不可变(真正的“仅通过赋值可变”)结构获得更好的性能和更清晰的语义。

顺便说一句,有时设计一个不可变对象很有用,这样生成一个稍微不同的对象会将所有内容从旧对象复制到新对象,但更改适当的部分,并且有时设计一个不可变对象很有用,以便稍微不同的对象将能够“重用”原始对象的大部分内容。在许多情况下,如果对象的读取频率远高于写入频率,则前一种方法将是更有效的方法,并且在对象更改后无需保留旧快照。在需要保留许多快照并且更新相对于读取访问频繁的情况下,后一种方法可能是更有效的方法。

The primary advantage of deeply immutable objects is that it's very easy to take a "snapshot" of their properties--simply copy a reference to the object. No matter how big or complicated the object might be, one can "snapshot" the whole thing simply by copying one reference.

By contrast, if one wants to take a snapshot of a mutable object's properties, it's necessary to copy all of them. If any of those properties are themselves mutable objects, it will be necessary to copy all of those as well. In some cases, making a usable copy of a mutable object's state can be very complicated or even impossible, since objects may have their state intertwined with those of singletons.

Although immutable objects are far easier to "snapshot" than mutable ones, it can sometimes be difficult to, given an immutable object, produce an instance which is similar to the first one except for some minor change. Mutable objects can sometimes be easier to work with in that regard. Sometimes it can be useful to copy data from an immutable object into a mutable object, change it, and then produce a new immutable object which holds the changed data. Unfortunately, there isn't any general means to automate such conversion with classes. There is, however, an alternative.

A struct with exposed fields which are all value primitives or immutable class references can offer a very convenient means of holding information in an immutable object, copying it to a mutable form, modifying it, and then making a new immutable object. Alternatively, one may copy the data in a struct easily by just copying the struct itself. Copying a struct which contains more than one or two int-sized fields is somewhat more expensive than copying an immutable-object reference, but copying a struct and changing it is generally much cheaper than making a new changed immutable object. It's important to note that because of some quirks in the way .net languages handle structs, some people regard mutable structs as evil. I would recommend that in general it's best for structs to simply expose their fields, and avoid having any methods (other than constructors) which mutate this. That will avoid most of the quirks associated with mutable structs, and will often offer better performance and clearer semantics than can be obtained with immutable classes, mutable classes, or so-called immutable (really "mutable by assignment only") structs.

Incidentally, it is sometimes useful to design an immutable object so that producing a slightly-different object will copy everything from the old object to a new one, but with the appropriate part changed, and it is sometimes useful to design an immutable object so that a slightly-different object will be able to "reuse" most of the original object. In many cases, the former approach will be the more efficient one if the object will be read much more often than it is written, and there isn't going to be any need to keep old snapshots around after an object has been changed. The latter approach can be the more efficient one in cases where one will want to keep around many snapshots, and updates are frequent relative to read accesses.

别理我 2024-09-02 23:31:01

那不是一个不可变的对象。不可

class ImmutableSomething : ISomething
{
    public readonly int SomeInt;

    public ImmutableSomething(int i)
    {
        SomeInt = i;
    }

    public ImmutableSomething AddValue(int add)
    {
        return new ImmutableSomething(this.SomeInt + add);
    }
}

变对象的主要好处是对象本身永远不会改变,因此您不会冒代码的一部分更改底层值的风险,特别是在多线程情况下,但这适用于一般的。这些保证通常使对象“更好”,因为您知道会发生什么,但没有什么能让不可变对象本质上比可变对象“更快”。

例如,DateTimes 是不可变的,因此您可以执行类似

DateTime someReferenceTime = DateTime.Now;

myBusinessLayer.DoABunchOfProcessingBasedOnTime(someReferenceTime);

// Here you are guaranteed that someReferenceTime has not changed, and you can do more with it.

Versus 之类的操作

StringBuilder sb = new StringBuilder("Seed");

myBusinessLayer.DoStuffBasedOnStringBuilder(sb);

// You have no guarantees about what sb contains here.

That's not an immutable object. An immutable version of this would be something like

class ImmutableSomething : ISomething
{
    public readonly int SomeInt;

    public ImmutableSomething(int i)
    {
        SomeInt = i;
    }

    public ImmutableSomething AddValue(int add)
    {
        return new ImmutableSomething(this.SomeInt + add);
    }
}

The main benefit of an immutable object is that the object itself will never change, so you don't risk one part of your code changing the underlying values, especially in multithreading situations, but this applies in general. These guarantees often makes objects "better" in that you know what to expect, but there's nothing that makes immutables inherently "faster" than mutable objects.

For example, DateTimes are immutable, so you can do stuff like

DateTime someReferenceTime = DateTime.Now;

myBusinessLayer.DoABunchOfProcessingBasedOnTime(someReferenceTime);

// Here you are guaranteed that someReferenceTime has not changed, and you can do more with it.

Versus something like

StringBuilder sb = new StringBuilder("Seed");

myBusinessLayer.DoStuffBasedOnStringBuilder(sb);

// You have no guarantees about what sb contains here.
笨死的猪 2024-09-02 23:31:01

抛开该示例实际上并未显示不可变对象这一点不谈,不可变对象的主要好处是它们使某些多线程操作变得非常简单且无锁。例如,在多线程环境中无需锁就可以枚举不可变的树结构,而如果树是可变的,则需要引入锁才能安全地枚举它。

但不可变对象并没有什么神奇之处,可以让它们本质上更快。

Leaving aside the point that the example doesn't actually show an immutable object, the main benefit for immutable objects is that they make certain multi-threaded operations dead simple and lock-free. For example, enumerating an immutable tree structure is possible without locks in a multi-threaded environment, whereas if the tree was mutable, you would need to introduce locks in order to safely enumerate it.

But there is nothing magical about immutable objects that makes them inherently faster.

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