【问题标题】:Locking an object to prevent any data changes锁定对象以防止任何数据更改
【发布时间】:2011-05-26 13:31:53
【问题描述】:

这有点奇怪……

假设我有以下课程:

public class Wibble
{
  public string Foo {get;set;}
  public string Bar {get;set;}
}

此类用于更新/更改 Foo 和 Bar 的值的过程。但是,在过程中的某个点之后,我想“锁定”实例以防止进行任何更改。那么问题是如何最好地做到这一点?

某种解决方案是这样的:

public class Wibble
{
  private string _foo;
  private string _bar;

  public bool Locked {get; set;}

  public string Foo
  {
    get
    {
      return this._foo
    }
    set
    {
        if (this.Locked)
        {
          throw new ObjectIsLockedException()
        }

        this._foo = value;
    }
  }

  public string Bar
  {
    get
    {
      return this._bar
    }
    set
    {
        if (this.Locked)
        {
          throw new ObjectIsLockedException()
        }

        this._bar = value;
    }
  }
}

然而这似乎有点不雅。

想要这样做的原因是我有一个应用程序框架,它使用使用该类的外部开发插件。 Wibble 类被传递到插件中,但是其中一些永远不应该更改内容,其中一些可以。这背后的目的是捕捉开发集成问题,而不是运行时生产问题。将对象“锁定”允许快速识别未按指定编码的插件。

【问题讨论】:

标签: c#


【解决方案1】:

我已经实现了类似于您的锁定模式的东西,但也具有由包含实际类数据的私有子类实现的只读接口,因此您可以传递显然是只读视图的数据并且不能向上转换为原始的“可变版本”。锁定纯粹是为了防止数据提供者在提供不可变视图后进行进一步的更改。

正如您所指出的,它运行得相当好,但有点尴尬。我认为拥有可变的“Builder”对象实际上更干净,然后可以生成不可变的快照。想想 StringBuilder 和 String。这意味着您复制了一些属性代码并且必须编写例程来进行复制,但在我看来,这比在每个属性上都有一个写锁更尴尬。在编译时也很明显,快照应该是只读的,并且 Builder 的用户不能修改它之前创建的快照。

【讨论】:

    【解决方案2】:

    我会推荐这个:

    一个不可变的基类:

    public class Wibble
    {
      public string Foo { get; private set; }
      public string Bar { get; private set; }
    
      public Wibble(string foo, string bar)
      {
        this.Foo = foo;
        this.Bar = bar
      } 
    }
    

    然后是一个可以更改的可变类,然后在时机成熟时创建一个不可变副本。

    public class MutableWibble 
    {
      public string Foo { get; set; }
      public string Bar { get; set; }
    
      public Wibble CreateImmutableWibble() 
      {
        return new Wibble(this.Foo, this.Bar);
      }
    }
    

    我不记得确切的 C# 语法,但你明白了。

    延伸阅读:http://msdn.microsoft.com/en-us/library/acdd6hb7%28v=vs.71%29.aspx

    【讨论】:

    • 我实际上不建议将 Wibble 作为基类,因为这只是引入了将 MutableWibble 像 Wibble 一样传递的可能性,这意味着给定的 Wibble 实例可能是也可能不是可变的。
    • @Dan:我修复了@Joe 的答案,并删除了基类,因为那永远不会按照他想要的方式工作。如果没有基类(以及对代码的一些更改),它现在可以工作了。
    • @Steven - 是的,这很愚蠢,感谢您的修复。我事后添加了readonlys。但我确实坚持拥有一个不可变的基类。 Scala 就是这样做的,例如使用 HashMap,Objective-C 可以做到这一点,例如使用 NSMutableArray (尽管实现非常混乱)。我不想卷入编辑大战,但我会坚持拥有一个不可变的基类。
    • @Dan 我不同意。如果您有一个Wibble 引用到一个MutableWibble 指针,那么具有Wibble 引用的代码无论如何都不会期望能够改变该对象。
    【解决方案3】:

    你不能让一个对象不可变!

    你可以关注这个帖子:

    How do I create an immutable Class?

    但我认为你总是可以通过反射来改变属性值!

    更新

    "...其实字符串对象不是这样的 不可变的,据我所知,至少有两种方法可以打破字符串 不变性。使用此代码示例所示的指针和一些高级 System.Reflection 用法...."

    http://codebetter.com/patricksmacchia/2008/01/13/immutable-types-understand-them-and-use-them/

    【讨论】:

    • 在 .NET 中,在完全信任的情况下运行,您可以为所欲为。您可以使用指针在您想要的任何位置写入。完全信任运行意味着您关闭了 .NET 的安全机制,它仍然需要反射或指针来写入不可变结构。这不是我们所说的“一成不变”,因为那是在欺骗自己,是在自取其辱。所以虽然我们实际上可以改变一切,但我们认为像strings 这样的东西是不可变的。甚至您提到的 Pattrick Smacchia 的文章也会调用 String immutable。
    • 是的,你是对的......但在我看来,如果可以选择做某事,你可以建议你可以做到。我认为有趣的是,通过反思你总是可以改变一个类。
    • 您可以随时使用内存操作更改地址空间中的任何内容。在某种程度上,您根据预期的使用模式对语言进行陈述。在某种程度上,关于 c# java 或任何东西的每一条陈述都可能被渲染为不正确,如果有人想去麻烦。
    【解决方案4】:

    您拥有的另一个选项是使用 BinaryFormatter 创建要“锁定”的对象的成员克隆。虽然您没有锁定对象,但您正在创建一个可以丢弃的快照,而原始对象保持不变。

    【讨论】:

      猜你喜欢
      • 2012-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多