【问题标题】:List<T> readonly with a private setList<T> 只读,带有私有集
【发布时间】:2011-01-20 15:30:22
【问题描述】:

如何公开List&lt;T&gt; 使其只读,但可以私下设置?

这不起作用:

public List<string> myList {readonly get; private set; }

即使你这样做:

public List<string> myList {get; private set; }

你仍然可以这样做:

myList.Add("TEST"); //This should not be allowed

我猜你可能有:

public List<string> myList {get{ return otherList;}}
private List<string> otherList {get;set;}

【问题讨论】:

    标签: c# .net generics c#-3.0


    【解决方案1】:

    我认为你在混合概念。

    public List<string> myList {get; private set;}
    

    已经“只读”了。也就是说,在这个类之外,没有任何东西可以将myList 设置为List&lt;string&gt; 的不同实例

    但是,如果您想要一个只读列表,如“我不希望人们能够修改列表内容”,那么您需要公开一个ReadOnlyCollection&lt;string&gt;。您可以通过以下方式执行此操作:

    private List<string> actualList = new List<string>();
    public ReadOnlyCollection<string> myList
    {
      get{ return actualList.AsReadOnly();}
    }
    

    注意,在第一个代码sn -p中,其他人可以操作List,但不能改变你拥有的list。在第二个sn-p中,别人会得到一个只读的列表,不能修改。

    【讨论】:

    • 好点。很多人错过了引用类型的概念并将引用设置为只读。 +1
    • 将 ReadOnlyCollection 作为 IList 会更好。
    • +1。返回公共 System.Collections.ObjectModel.ReadOnlyCollection 比在公共属性上使用“私有集”更好,因为它可以让您进行编译时检查。这意味着在只读集合上使用 Add() 或 RemoveAt() 时会引发编译错误,而“私有集”不会。
    • @MikeTeeVee private set; 不会导致 Add()RemoteAt() 引发编译时错误运行时错误。使用类外部的私有 setter 将内容添加到 List 是完全合法的。这就是为什么 Philip 建议使用 ReadOnlyCollection 的第二种解决方案来防止外部修改以列出需要此类行为的内容。
    • @Dan 请重新阅读我的评论,因为这正是我提出的观点。抱歉,如果不清楚。
    【解决方案2】:

    如果您想要只读集合,请使用ReadOnlyCollection&lt;T&gt;,而不是List&lt;T&gt;

    public ReadOnlyCollection<string> MyList { get; private set; }
    

    【讨论】:

      【解决方案3】:

      我更喜欢使用 IEnumerable

      private readonly List<string> _list = new List<string>();
      
      public IEnumerable<string> Values // Adding is not allowed - only iteration
      {
         get { return _list; }
      }
      

      【讨论】:

      • 这是一个很好的方法,只是它有缺陷: MyClass m = new MyClass(); List x = m.Values as List; x.Add("测试"); foreach (m.Values 中的字符串 s) { Console.WriteLine(s); } //这允许我通过强制转换为其添加值。
      • 嗯,这是一个 hack :) 您还可以使用反射将元素添加到内部集合。 IEnumerable 对 MyClass 客户端隐藏了实现。你确定我没有在里面使用 LinkedList 吗?
      • 您可以随时反思以找到收藏类型,因此我将始终确定。如果你要这样做,那就做对。当然,任何人都可以使用反射来“破解”对象以添加项目,但您对此无能为力。练习正确的技术并正确使用框架是您所能做的。
      • 1) 尝试将接口转换为某个类 - 接口的使用不正确,您应该踢掉这样做的人 2) 产量声明有什么问题?
      • 如果 API 提供 ICollection,从 ICollection 转换为 List 是一种技巧。
      【解决方案4】:

      返回一个ReadOnlyCollection,它实现了IList

      private List<string> myList;
      
      public IList<string> MyList
      {
        get{return myList.AsReadOnly();}
      }
      

      【讨论】:

        【解决方案5】:

        有一个名为 ReadOnlyCollection&lt;T&gt; 的集合 - 这就是您要找的吗?

        【讨论】:

          【解决方案6】:

          您可以使用 List 的 AsReadOnly() 方法返回一个只读包装器。

          【讨论】:

            【解决方案7】:
            private List<string> my_list;
            
            public ReadOnlyCollection<string> myList
            {
                get { return my_list.AsReadOnly(); }
                private set { my_list = value; }
            }
            

            【讨论】:

              【解决方案8】:
                  private List<string> _items = new List<string>();         
              
                  public ReadOnlyCollection<string> Items
              
                  {
              
                      get { return _items.AsReadOnly(); }
              
                      private set { _items = value }
              
                  }
              

              【讨论】:

                【解决方案9】:

                这是一种方法

                public class MyClass
                    {
                        private List<string> _myList;
                        public ReadOnlyCollection<string> PublicReadOnlyList { get { return _myList.AsReadOnly(); } }
                
                        public MyClass()
                        {
                            _myList = new List<string>();
                            _myList.Add("tesT");
                            _myList.Add("tesT1");
                            _myList.Add("tesT2");
                
                            //(_myList.AsReadOnly() as List<string>).Add("test 5");
                        }
                
                    }
                

                【讨论】:

                • 嗯...我认为您可以将 PublicReadOnlyList 与 getter new ReadOnlyCollection(_myList) 一起使用
                【解决方案10】:

                在 .NET 4.5 框架中,您只能公开 IReadOnlyList 接口。 比如:

                private List<string> _mylist;
                public IReadOnlyList<string> myList { get {return _myList;} }
                

                或者如果你想防止不必要的转换为 IList

                private List<string> _mylist;
                public IReadOnlyList<string> myList { get {return new List<string>(_myList);} }
                

                【讨论】:

                  【解决方案11】:
                  private List<string> myList;
                  
                  public string this[int i]
                  {
                      get { return myList[i]; }
                      set { myList[i] = value; }
                  }
                  

                  【讨论】:

                    【解决方案12】:

                    为什么要使用列表。听起来您真正想要公开的是 IEnumerable

                    public IEnumerable<string> myList { get; private set; }
                    

                    现在该类的用户可以阅读项目但不能更改列表。

                    【讨论】:

                    • 有缺陷!您可以将其转换为 List 并添加值: MyClass m = new MyClass(); List x = m.Values as List; x.Add("测试"); foreach (m.Values 中的字符串 s) { Console.WriteLine(s); }
                    • 是的,你可以做很多事情。当然,您始终可以将 myList 设置为 List 以外的其他值,在这种情况下代码会中断。我通常不担心人们故意忽略封装引起的问题。
                    • @DustinDavis,将暴露的类型转换为您知道实际使用的实现是破坏封装的定义的另一种类型。
                    • @DustinDavis,消费者当然可以打破封装。他们忽略您的 API 提供的内容不会以任何方式伤害您。就像我卖给你一辆有保修的汽车,你去用巨大的甜甜圈换轮胎。你可以做到,但我不保证结果。
                    • 刚刚从 Bill Wagner 的“Effective C# 50 Specific...”中发现了这一点:“在 List 中公开 IEnumerable 接口就是这种策略的一个例子。Machiavellian那里的程序员可以通过猜测实现接口的对象的类型并使用强制转换来解决这个问题。但是那些为了创造错误而付出那么多工作的程序员会得到他们应得的。第 155 页
                    【解决方案13】:

                    您也可以创建普通列表,但通过 IEnumerable 类型的属性公开它

                    private List<int> _list = new List<int>();
                    
                    public IEnumerable<int> publicCollection{
                       get { return _list; }
                    }
                    

                    【讨论】:

                    • 有缺陷!您可以将其转换为 List 并添加值: MyClass m = new MyClass(); List x = m.Values as List; x.Add("测试"); foreach (m.Values 中的字符串 s) { Console.WriteLine(s); }
                    • 确实 - 令我惊讶的是,即使 _list.AsEnumerable() 也可以转换为 List。
                    • 任何依赖于能够将IEnumerable&lt;T&gt; 转换为List&lt;T&gt; 的人都是愚蠢的。
                    【解决方案14】:

                    有点晚了,但尽管如此:我不喜欢使用 ReadOnlyCollection 包装器,因为它仍然公开了所有修改集合的方法,当在运行时访问这些方法时都会抛出 NotSupportedException。换句话说,它实现了IList 接口,但在运行时违反了同样的约定。

                    为了表达我确实在公开一个不可变列表,我通常会使用自定义的IIndexable 接口,它将长度和索引器添加到IEnumerable(在this CodeProject article 中描述)。它是一个包装器,因为它应该首先完成恕我直言。

                    【讨论】:

                      【解决方案15】:

                      我还没有看到这个选项:

                      private List<string> myList;
                      
                      public List<string> MyList
                      {
                          get { return myList.AsReadOnly().ToList(); }
                      }
                      

                      这应该允许您公开一个只读列表。

                      【讨论】:

                      • 这不好。首先,您正在创建一个ReadOnlyCollection&lt;T&gt; 来包装列表。这是次优的,因为ReadOnlyCollection&lt;T&gt; 的一个实例实现了可变的IList&lt;T&gt; 接口,并且如果它被修改会在运行时抛出。但是,您在将其所有内容复制到新的List&lt;T&gt; 后丢弃该集合,这将允许修改,尽管不是原始列表。此外,此代码具有误导性,因为它公开了一个可变成员,该成员旨在是不可变的,但实际上是一个意外的可变副本。
                      猜你喜欢
                      • 1970-01-01
                      • 2011-04-21
                      • 1970-01-01
                      • 2012-04-29
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-01-23
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多