不变性的真正定义?

时间:2009-05-26 21:28:26

标签: c# .net immutability

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

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

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

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

10 个答案:

答案 0 :(得分:23)

  

如果值未公开,那么无法修改,那就足够了吗?

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

  

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

不,因为那仍然是突变。

  

或者只能将它们设置在构造函数中?

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

  

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

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

答案 1 :(得分:7)

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

Eric Lippert在他的博客中撰写了一系列关于C#不可变性的帖子,你可能会感兴趣:you find the first part here

答案 2 :(得分:5)

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

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

说实话,我想不出有多少理由可能会或者需要这种“看不见的可变性”。

答案 3 :(得分:3)

以下是维基百科(link

的不变性定义

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

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

答案 4 :(得分:3)

那里有很多问题。我会尝试单独回答每个问题:

  • “我想知道如何定义不变性?” - 直接来自Wikipedia page(以及完全准确/简洁的定义)

      

    不可变对象是一个对象,其状态在创建后无法修改

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

  • “可以在类型内修改值,而不是类型的客户吗?” - 从技术上讲,它不能在内部或由类型的消费者修改。在实践中,存在System.String(此事项的参考类型)等类型,几乎所有实际目的都可以认为是可变的,但理论上并非如此。

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

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

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

    1. 不要暴露任何变量。它们应该全部保留private(甚至protected都不可接受,因为派生类可以修改状态)。
    2. 不允许任何实例方法修改状态(变量)。这应该只在构造函数中完成,而方法应该使用特定的构造函数创建新实例,如果它们需要返回“已修改”的对象。
    3. 所有暴露(作为只读)或方法返回的对象的成员必须不可变。
    4. 注意:您无法确保派生类型的不变性,因为它们可以定义新变量。这是标记任何类型的原因,你要确保它不可变为sealed,这样在代码中的任何地方都不能认为派生类是你的基本不可变类型。

希望有所帮助。

答案 5 :(得分:1)

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

答案 6 :(得分:1)

不变性的定义可以位于Google

示例:

  

不可变 - 字面意思是,无法改变。
  www.filosofia.net/materiales/rec/glosaen.htm

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

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

答案 7 :(得分:1)

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

答案 8 :(得分:1)

遗憾的是,c#/ vb.net中没有不可变的关键字,虽然它已被争论,但是如果没有autoproperties并且所有字段都用readonly声明(readonly字段只能在构造函数中分配)modfier和所有字段声明是一种不可改变的类型,你将确保自己的不变性。

答案 9 :(得分:1)

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

然而,其他答案忽略的一点是对象状态的定义。如果Foo是一个类,则List<Foo>的状态由其中包含的对象标识的序列组成。如果对特定List<Foo>实例的唯一引用由代码保留,既不会导致更改该序列,也不会将其暴露给可能执行此操作的代码,那么该实例将是不可变的,无论{{{其中提到的对象是可变的或不可变的。

使用类比,如果有一个汽车VIN列表(车辆识别码)印在防篡改纸上,列表本身就是不可变的,即使汽车没有。即使列表今天包含十辆红色汽车,明天也可能包含十辆蓝色汽车;然而,他们仍然是相同的十辆车。