8

我想知道如何定义不变性?如果这些值没有公开,所以不能修改,那么就足够了吗?

可以在类型内部修改值,而不是由该类型的客户修改吗?

还是只能将它们设置在构造函数中?如果是这样,在双重初始化的情况下(this在结构上使用关键字等)对于不可变类型仍然可以吗?

如何保证类型是 100% 不可变的?

4

10 回答 10

23

如果这些值没有公开,所以不能修改,那么就足够了吗?

不,因为您需要读取权限。

可以在类型内部修改值,而不是由该类型的客户修改吗?

不,因为那仍然是突变。

还是只能将它们设置在构造函数中?

叮叮叮!另外一点,不可变类型通常具有构造和返回新实例的方法,并且通常还具有internal专门标记为供这些方法使用的额外构造函数。

如何保证类型是 100% 不可变的?

在 .Net 中,获得这样的保证很棘手,因为您可以使用反射来修改(变异)私有成员。

于 2009-05-26T21:31:29.647 回答
7

之前的海报已经声明你应该在构造函数中为你的字段赋值,然后不要动手。但这有时说起来容易做起来难。假设您的不可变对象公开了 type 的属性List<string>。该列表是否允许更改?如果没有,你将如何控制它?

Eric Lippert 在他的博客中写了一系列关于 C# 中的不变性的文章,您可能会觉得有趣:您可以在这里找到第一部分

于 2009-05-26T21:36:44.503 回答
5

我认为在所有这些答案中可能会遗漏的一件事是,我认为即使对象的内部状态发生变化,对象也可以被认为是不可变的——只要这些内部更改对“客户端”代码不可见。

例如,System.String该类是不可变的,但我认为允许缓存实例的哈希码,因此仅在第一次调用GetHashCode(). 请注意,据我所知,System.String该类不这样做,但我认为它可以并且仍然被认为是不可变的。当然,这些更改中的任何一个都必须以线程安全的方式处理(与更改的不可观察方面保持一致)。

不过老实说,我想不出很多人可能想要或需要这种“隐形可变性”的原因。

于 2009-05-27T00:35:34.627 回答
3

这是维基百科中不变性的定义(链接

“在面向对象和函数式编程中,不可变对象是在创建后状态无法修改的对象。”

本质上,一旦创建了对象,就不能更改其任何属性。一个例子是 String 类。一旦创建了 String 对象,它就无法更改。对其进行的任何操作实际上都会创建一个新的 String 对象。

于 2009-05-26T21:32:03.000 回答
3

那里有很多问题。我将尝试分别回答每个问题:

  • “我想知道不变性是如何定义的?” - 直接来自维基百科页面(以及完全准确/简洁的定义)

    不可变对象是创建后状态不能修改的对象

  • “如果这些值不公开,所以不能修改,就够了吗?” - 不完全的。它不能以任何方式修改,因此您必须确保方法/函数不会改变对象的状态,并且如果执行操作,请始终返回一个新实例。

  • “可以在类型内部修改值,而不是由该类型的客户修改吗?” - 从技术上讲,它不能在内部或由该类型的消费者修改。在实践中,存在诸如System.String(事物的引用类型)之类的类型,尽管在理论上不是这样,但对于几乎所有实际目的而言,它们都可以被认为是可变的。

  • “或者只能将它们设置在构造函数中?” - 是的,理论上这是唯一可以设置状态(变量)的地方。

  • “如果是这样,在双重初始化的情况下(在结构上使用 this 关键字等)对于不可变类型仍然可以吗?” - 是的,这仍然很好,因为它都是初始化(创建)过程的一部分,并且实例在完成之前不会返回。

  • “我如何保证类型是 100% 不可变的?” - 以下条件应确保这一点。(如果我错过了,请有人指出。)

    1. 不要暴露任何变量。它们都应该保留private(甚至protected不可接受,因为派生类可以修改状态)。
    2. 不允许任何实例方法修改状态(变量)。这应该只在构造函数中完成,而如果方法需要返回“修改”对象,则应该使用特定的构造函数创建新实例。
    3. 所有公开(只读)的成员或方法返回的对象本身必须是不可变的。

    注意:您不能确保派生类型的不变性,因为它们可以定义新变量。这是标记任何您不想确保它不可变的类型的原因,sealed以便在代码中的任何地方都不能将派生类视为您的基本不可变类型。

希望有帮助。

于 2009-05-26T21:36:04.287 回答
1

我了解到,不变性是指您在构造函数中设置了所有内容,并且以后无法在对象的生命周期内对其进行修改。

于 2009-05-26T21:32:47.663 回答
1

不变性的定义可以在Google上找到。

例子:

不可变 - 从字面上看,无法改变。
www.filosofia.net/materiales/rec/glosaen.htm

就不可变数据结构而言,典型的定义是一次写入多次读取,也就是说,正如你所说,一旦创建,就无法更改。

有些情况稍微处于灰色区域。例如,.NET 字符串被认为是不可变的,因为它们不能更改,但是 StringBuilder 在内部修改了一个 String 对象。

于 2009-05-26T21:33:49.970 回答
1

不可变的本质上是一个类,它在自己的代码中强制自己成为最终的。一旦它在那里,什么都不能改变。据我所知,事情是在构造函数中设置的,然后就是这样。我看不出有什么东西是不可变的。

于 2009-05-26T21:34:27.520 回答
1

不幸的是,c#/vb.net 中没有不可变的关键字,尽管它一直在争论,但是如果没有自动属性并且所有字段都用只读(只读字段只能在构造函数中分配)modfier 声明并且所有字段都声明为一个不可变的类型,你将确保你的自我不变性。

于 2009-05-27T06:06:58.280 回答
1

不可变对象是其可观察状态永远不会被任何合理的代码执行序列改变的对象。不可变类型是一种保证暴露给外部世界的任何实例都是不可变的(这个要求通常被声明为要求对象的状态只能在其构造函数中设置;这对于具有私有构造函数,对于在构造过程中调用自身外部方法的对象来说也是不够的)。

然而,其他答案忽略的一点是对象状态的定义。如果Foo是一个类,则 a 的状态由其中包含的对象身份List<Foo>序列组成。如果对特定实例的唯一引用由既不会导致该序列更改,也不会将其暴露给可能这样做的代码的代码持有,那么该实例将是不可变的,无论其中引用的对象是可变的还是不可变的。List<Foo>Foo

打个比方,如果一个人在防篡改纸上打印了一份汽车 VIN(车辆识别码)列表,那么即使汽车不是,该列表本身也是不可变的。即使列表今天包含 10 辆红色汽车,明天也可能包含 10 辆蓝色汽车;然而,它们仍然是同样的十辆汽车

于 2012-08-14T22:59:33.027 回答