【问题标题】:A ReadOnlyCollection isn't readonly. How do i create a true ReadOnlyCollection?ReadOnlyCollection 不是只读的。如何创建真正的 ReadOnlyCollection?
【发布时间】:2011-03-01 13:02:54
【问题描述】:

我知道 readonly 集合会阻止从列表中添加/删除,但它为什么不阻止设置集合中对象的属性。

 System.Collections.ObjectModel.ReadOnlyCollection<PersonPhoneNumber> ReadOnlyPhoneNumbers = new System.Collections.ObjectModel.ReadOnlyCollection<PersonPhoneNumber>(_PhoneNumbers);
            ReadOnlyPhoneNumbers[0].Number = "01111111111111";

就本问题而言,假设 _PhoneNumbers 是一个 List,并且它包含至少一个 PersonPhoneNumber 类的实例。

如何公开对象集合并使对象只读?此问题的根源在于必须在 WCF 数据合同中公开私有集合,但我不希望该集合可访问。

我想使用:

  Person.Mobile = "011111111111111";

代替:

Person.PhoneNumbers.Add(New PersonPhoneNumber{Number= "01111111111111", Type=Mobile});

【问题讨论】:

    标签: .net wcf readonly-collection


    【解决方案1】:

    您不能将对象设为只读。那将需要更改类型。如果您确实需要提供只读对象列表,则需要为您想要公开的每种类型创建某种不可变的包装类型(或从一开始就使您的类型不可变)。当然,这将是一个单独的类型,如果您需要它们互操作,那将是相当复杂的...

    【讨论】:

      【解决方案2】:

      如何阻止这种情况发生?您将如何尝试在 C# 中构建这样的东西?如果它支持克隆,它可能会复制从索引器(等)返回的任何内容 - 但随后对象将需要执行克隆。你希望你刚刚给出的代码会发生什么?它应该抛出异常吗?如果是这样,那就是对 PersonPhoneNumber 类本身的更改。

      听起来您应该基本上将您的 PersonPhoneNumber 类更改为不可变的。如果您需要一种以可变方式构建它的方法,您可以创建一个知道如何创建 PersonPhoneNumber 的构建器类型 - 例如:

      var ppn = new PersonPhoneNumber.Builder { /* set properties here }.Build();
      

      不过,对于相当简单的类型,编写适当的构造函数会更容易。

      【讨论】:

      • 理想情况下,如果这个属性被序列化过程以外的任何东西访问,我想抛出一个异常。我想实现一个只读属性,但不能这样做,因为序列化过程需要每个属性的 Get 和 Set 方法。
      【解决方案3】:

      这个怎么样:

      1. 为您的 PersonPhoneNumber 创建一个只公开只读属性(没有设置器)的接口
      2. 您创建一个 IPersonPhoneNumber 的只读集合,而不是 PersonPhoneNumber 集合,但将您的 PersonPhoneNumber 项放入其中

      当然,这不会阻止代码将您的项目转换为 PersonPhoneNumber,但它确实可以防止意外修改项目的属性

      【讨论】:

        【解决方案4】:

        由于PersonPhoneNumber 是一个类,ReadOnlyCollection 让您可以访问任何PersonPhoneNumber 对象的引用。由于您引用了PersonPhoneNumber 对象,因此您可以执行PersonPhoneNumber 对象允许您执行的任何操作,而它来自ReadOnlyCollection 的事实并不相关。

        您可以考虑将PersonPhoneNumber 改为结构。如果这样做,那么当您从集合中请求索引对象时,框架会将其装箱以避免复制整个结构(为了提高效率)。但是,如果您随后尝试对其进行修改,则会收到错误“无法修改拆箱转换的结果”:

        ReadOnlyPhoneNumbers[0].Number = "01111111111111";
        // Cannot modify the result of an unboxing conversion
        

        你可以将它分配给一个局部变量,但这会产生一个副本:

        PersonPhoneNumber ppn = ReadOnlyPhoneNumbers[0].Number;
        ppn.Number = "01111111111111";
        

        ppn 是一个副本。 ReadOnlyCollection 中的原始内容未修改。

        (当然,在将PersonPhoneNumber 更改为结构之前,您需要考虑此更改的其他后果。可能还有其他原因导致结构不合适。)

        【讨论】:

          【解决方案5】:

          问题是使对象在进入时只读 - 为了使这些对象只读,您需要将它们包装在某种不允许设置值的代理中。

          Castle 的动态代理可能允许您将对象包装在您自己实现的 ReadOnlyProxy 中。然后,当它们进入集合时,您的 add 方法必须将所有内容包装在 ReadOnlyProxy 中。

          不好...

          【讨论】:

            【解决方案6】:

            你想要的不是收藏的问题。该集合包含对您的项目的引用,您可以通过访问该集合来获取这些项目。
            您可以尝试在插入之前克隆您的对象。这不会阻止它们被更改,但原始对象将被保留。否则,您必须编写只读包装器或为您的对象选择另一种构造。

            【讨论】:

              【解决方案7】:

              你可能想看看这篇文章。

              C# Generics Recipes—Making Read-Only Collections the Generic Way

              我认为您想要的是将电话号码列表保留为真正的私有数据成员,然后通过人员对象上的设置器和获取器公开对各种类型电话号码的访问。如果该人只能拥有每种类型中的一种,这将很好用……否则您将不得不将其更改为一种方法。比如 .AddMobile("011111")

              【讨论】:

              • 它并不理想,但我正在考虑改用操作/方法。
              猜你喜欢
              • 2020-11-14
              • 1970-01-01
              • 2012-12-15
              • 1970-01-01
              • 2015-12-03
              • 1970-01-01
              • 1970-01-01
              • 2017-12-23
              • 1970-01-01
              相关资源
              最近更新 更多