【问题标题】:"Read only" Property Accessor in C#C# 中的“只读”属性访问器
【发布时间】:2010-10-11 18:55:27
【问题描述】:

我有以下课程:

class SampleClass
{
   private ArrayList mMyList;

   SampleClass()
   {
       // Initialize mMyList
   }

   public ArrayList MyList
   {
       get { return mMyList;}
   }
}

我希望用户能够获取 mMyList,这就是为什么我通过属性公开“get”但我不希望他们对对象进行更改(即 MyList.Add(new Class());)回到我的课堂。

我想我可以返回对象的副本,但这可能会很慢,我正在寻找一种方法来提供编译时错误,通知用户他们不应该期望能够修改返回的属性的值。

这可能吗?

【问题讨论】:

标签: c# .net properties readonly accessor


【解决方案1】:

可从 .NET v2.0 获得

    public ArrayList MyList { get; private set; }

【讨论】:

  • 您仍然可以在现有列表上使用 Add。
【解决方案2】:

【讨论】:

    【解决方案3】:

    使用ReadOnlyCollection

    return new ReadOnlyCollection<object>(mMyList).

    您还可以将ReadOnlyCollection 设为字段,它会自动反映基础列表中的更改。这是最有效的解决方案。

    如果您知道 mMyList 只包含一种类型的对象(例如,它只有 DateTimes), 您可以返回 ReadOnlyCollection<DateTime>(或其他类型)以获得额外的类型安全性。如果您使用的是 C# 3,您可以简单地编写

    return new ReadOnlyCollection<DateTime>(mMyList.OfType<DateTime>().ToArray())

    但是,这不会自动更新底层列表,效率也较低(它会复制整个列表)。最好的选择是使 mMyList 成为一个通用的List<T>(例如,一个List<DateTime>

    【讨论】:

      【解决方案4】:

      只是为了扩展 JaredPar 的答案。正如他所说,返回实际的支持字段并不完全安全,因为用户仍然可以将IEnumerable 动态转换回ArrayList

      我会写这样的东西来确保不对原始列表进行任何修改:

      public IEnumerable MyList
      {
          get
          {
               foreach (object o in mMyList)
                   yield return o;
          }
      }
      

      再说一次,我可能还会使用通用列表 (IEnumerable<T>) 来保证类型完全安全。

      【讨论】:

      • 这个是正确的——希望我能投票两次。循环后不调用yield break;有什么问题吗?
      • @Jay:循环后不需要明确的yield break;。该方法的结束意味着Enumerator 的结束(并且MoveNext 将正确返回false)。
      • 使用此解决方案,用户不会失去 ArrayList 的功能,例如快速随机访问和快速计数吗?
      • @ColonelPanic 是的!更糟糕的是,每次访问IEnummerable(例如做obj.MyList.Count())时,get中的代码都会被执行并再次遍历列表。
      【解决方案5】:

      只需将属性设置为 Sealed(或 VB.NET 中的 NotInheritable)并且只读,如下所示:

         public sealed readonly ArrayList MyList
         {
             get { return mMyList;}
         }
      

      另外不要忘记将支持字段设置为只读:

         private readonly ArrayList mMyList;
      

      但是为了初始化 mMyList 的值,你必须只在构造函数中初始化它,如下例所示:

      public SampleClass()
      {
         // Initialize mMyList
         mMyList = new ArrayList();
      }
      

      【讨论】:

        【解决方案6】:

        使用ArrayList.ReadOnly() 方法构造并返回列表周围的只读包装器。它不会复制列表,而只是在其周围创建一个只读包装器。为了获得编译时检查,您可能希望按照@Jared 的建议将只读包装器返回为 IEnumerable。

        public IEnumerable MyList
        {
             get { return ArrayList.ReadOnly(mMyList); }
        }
        

        只读的集合是 只是一个带有包装器的集合 这可以防止修改 收藏。如果对 底层集合,只读 集合反映了这些变化。

        此方法是 O(1) 操作。

        Reference

        【讨论】:

        • 我反对这个解决方案,因为它将应该是编译时错误变成运行时错误。
        • -1, OP:“我正在寻找一种能够提供编译时错误的方法”
        • 在不复制到 ReadOnlyCollection 的情况下——他还说他想避免这种情况——我选择强制只读而不是假装只读。两者都做实际上可能更好。
        • +1,看起来你已经修复了它,所以如果他们不强制转换它会产生编译时错误,如果他们强制转换则会产生运行时错误,而不会牺牲 O(1) 的性质。不错。
        【解决方案7】:

        您应该将返回值包装在只读集合中。

        【讨论】:

          【解决方案8】:

          使用 ArrayList,您会受到相当大的限制,因为 BCL 中没有只读的非泛型集合类。快速而肮脏的解决方案是返回一种 IEnumerable。

             public IEnumerable MyList
             {
                 get { return mMyList;}
             }
          

          这实际上不会阻止某人投射到 ArrayList,但默认情况下也不允许编辑

          您可以通过调用 ArrayList.ReadOnly 返回一个有效的只读列表。但是它的返回类型是 ArrayList,因此用户仍然可以使用 .Add 进行编译,但会产生运行时错误。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-18
            • 1970-01-01
            • 2014-08-22
            • 1970-01-01
            • 2011-06-02
            • 2016-08-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多