zoukankan      html  css  js  c++  java
  • c# —— readonly

    转载:http://www.cnblogs.com/abatei/archive/2008/02/04/1064102.html

    使用泛型创建只读集合

    问题

    您希望类中的一个集合里的信息可以被外界访问,但不希望用户改变这个集合。

    解决方案

    使用ReadOnlyCollection<T>包装就很容易实现只读的集合类。例子如,Lottery类包含了中奖号码,它可以被访问,但不允许被改变:

    public class Lottery
        {
            // 创建一个列表.
            List<int> _numbers = null;
            public Lottery()
            {
                // 初始化内部列表
                _numbers = new List<int>(5);
                // 添加值
                _numbers.Add(17);
                _numbers.Add(21);
                _numbers.Add(32);
                _numbers.Add(44);
                _numbers.Add(58);
            }
            public ReadOnlyCollection<int> Results
            {
                // 返回一份包装后的结果
                get { return new ReadOnlyCollection<int>(_numbers); }
            }
    }

    Lottery有一个内部的List<int>,它包含了在构造方法中被填的中奖号码。有趣的部分是它有一个公有属性叫Results,通过返回的ReadOnlyCollection<int>类型可以看到其中的中奖号码,从而使用户通过返回的实例来使用它。

    如果用户试图设置集合中的一个值,将引发一个编译错误:

    Lottery tryYourLuck = new Lottery();
        // 打印结果.
        for (int i = 0; i < tryYourLuck.Results.Count; i++)
        {
            Console.WriteLine("Lottery Number " + i + " is " + tryYourLuck.Results[i]); 
        }
        // 改变中奖号码!
        tryYourLuck.Results[0]=29;
        //最后一行引发错误:// Error 26 // Property or indexer
        // 'System.Collections.ObjectModel.ReadOnlyCollection<int>.this[int]'
        // cannot be assigned to -- it is read only

    讨论

    ReadOnlyCollection的主要优势是使用上的灵活性,可以在任何支持IList或IList<T>的集合中把它做为接口使用。ReadOnlyCollection还可以象这样包装一般数组:

    int [] items = new int[3];
        items[0]=0;
        items[1]=1;
        items[2]=2;
    new ReadOnlyCollection<int>(items);

    这为类的只读属性的标准化提供了一种方法,并使得类库使用人员习惯于这种简单的只读属性返回类型。

    Use of IReadOnlyDictionary and IReadOnlyList Properties in .NET Objects

    Introduction

    The principles of object oriented software development identify the encapsulation of computing logic that is unique to a problem space and is the sole authority of that computation. The objective is the normalization of computing logic into discrete computing components that can be orchestrated into complex systems by using easier to manage and understand units.

    The interface to any object consists of the exposed properties and methods that allow other objects to interact with it. Unfortunately, many times, standard structure properties such as dictionaries and lists, are exposed creating a temptation to manipulate the data in these properties, which create a major violation of control.

    In fact, you will recognize “IDictionary” was written with the exposed read only collections “Keys” and “Values” instead of an actual list. One could only imagine the complications if the IDictionary exposes the real list in “Keys” and then the caller started adding to the list. Although one can argue that the list of keys does not really exist in the implementation of IDictionary, it is easy to see where this can be an issue.

    Approach

    Read only properties of .NET objects are a fundamental principle of any quality software development, which allows an object to maintain control of the data it is tasked to manage.

    Unfortunately, it is not uncommon to have a complex property that is a dictionary or list, which exposes the data of the object to changes outside the control of the object.

    The .NET Framework 4.5 finally provided us “IReadOnlyDictionary<TKey, TValue>” and “IReadOnlyList<TKey, TValue>” to rectify this. Now all objects that expose these structures should use them instead as follows:

    public class ExampleClass
    {
        public class ExampleData
        {
            public int ID { get; private set; }
            public string Value { get; private set;  }
            public ExampleData(int id, string value)
            {
                ID = id;
                Value = value;
            }
        }
    
        private Dictionary<string, ExampleData> _keyData;
        public IReadOnlyDictionary<string, ExampleData> KeyData { get { return _keyData; } }
    
        private List<ExampleData> _listData;
        public IReadOnlyList<ExampleData> ListData { get { return _listData; } }
    
        public ExampleClass()
        {
            _keyData = new Dictionary<string, ExampleData>();
            _listData = new List<ExampleData>();
        }
    
       // Implementation...
    }
    

      

    Using this coding pattern prevents manipulation of these data structures outside the control of the class, while providing direct access to the data within these structures.

    Unfortunately, not all objects that implement the “IDictionary<TKey, TValue>” or “IList<TKey>” interface also implements the corresponding “IReadOnlyDictionary<TKey, TValue>” or “IReadOnlyList<TKey>” interfaces.

    An example of this was the original implementation of “ConcurrentDictionary<TKey, TValue>” and properties of various collections such as “IDictionary<TKey, TValue>.Values”. To achieve this, you need a wrapper object that limits the interface of these structures to the read-only interfaces such as the following:

    /// <summary>
    /// Wrapper class for a IDictionary object to implement the IReadOnlyDictionary
    /// interface for the dictionary.
    /// </summary>
    /// <typeparam name="TKey">Data type for the dictionary key.</typeparam>
    /// <typeparam name="TValue">Data type for the dictionary value.</typeparam>
    public class ReadOnlyDictionaryWrapper<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
    {
        private IDictionary<TKey, TValue> _dictionary;
    
        public ReadOnlyDictionaryWrapper(IDictionary<TKey, TValue> dictionary)
        {
            _dictionary = dictionary;
        }
        public TValue this[TKey key] { get { return _dictionary[key]; } }
        public int Count { get { return _dictionary.Count; } }
        public IEnumerable<TKey> Keys { get { return _dictionary.Keys; } }
        public IEnumerable<TValue> Values { get { return _dictionary.Values; } }
        public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _dictionary.GetEnumerator(); }
        public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); }
        IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_dictionary).GetEnumerator(); }
    }
    /// <summary>
    /// Wrapper class for IList object to implement the IReadOnlyList interface for the list.
    /// </summary>
    /// <typeparam name="TValue">Data type of the list value.</typeparam>
    public class ReadOnlyListWrapper<TValue> : IReadOnlyList<TValue>
    {
        private IList<TValue> _list;
    
        public ReadOnlyListWrapper(IList<TValue> list)
        {
            _list = list;
        }
        public TValue this[int index] { get { return _list[index]; } }
        public int Count { get { return _list.Count; } }
        public IEnumerator<TValue> GetEnumerator() { return _list.GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_list).GetEnumerator(); }
    }
    

    This allows any “IDictionary<TKey, TValue>” or “IList<TKey>” object to be published as a read only object as in the following example:

    public class ExampleClass
    {
        public class ExampleData
        {
            public int ID { get; private set; }
            public string Value { get; private set; }
            public ExampleData(int id, string value)
            {
                ID = id;
                Value = value;
            }
        }
    
        private Dictionary<string, ExampleData> _keyData;
        public IReadOnlyDictionary<string, ExampleData> KeyData { get; private set; }
    
        private List<ExampleData> _listData;
        public IReadOnlyList<ExampleData> ListData { get; private set; }
    
        public ExampleClass()
        {
            _keyData = new Dictionary<string, ExampleData>();
            KeyData = new ReadOnlyDictionaryWrapper<string, ExampleData>(_keyData);
            _listData = new List<ExampleData>();
            ListData = new ReadOnlyListWrapper<ExampleData>(_listData);
        }
    
        // Implementation...
    }
    

      

    The .NET Framework does implement wrapper classes in the namespace “System.Collections.ObjectModel” called “ReadOnlyDictionary” and “ReadOnlyCollection”, but unfortunately these objects are designed to make a copy of the data structure properties, such as “Keys” and “Values”, when created.

    The advantage of the wrapper classes “ReadOnlyDictionaryWrapper” and “ReadOnlyListWrapper” above is the implementation is very lightweight, continues to track changes in the underlying structure and the users of the object can maintain references to the object.

    The disadvantage of these objects is the same as the original object, the underlying dictionary or list structure may change while iterating through the structure.

    This can be easily resolved by creating copy objects that derived from these wrapper objects as such:

    /// <summary>
    /// Read only copy of an IDictionary object.
    /// </summary>
    /// <typeparam name="TKey">Data type for the dictionary key.</typeparam>
    /// <typeparam name="TValue">Data type for the dictionary value.</typeparam>
    public class ReadOnlyDictionaryCopy<TKey, TValue> : ReadOnlyDictionaryWrapper<TKey, TValue>
    {
        public ReadOnlyDictionaryCopy(IEnumerable<KeyValuePair<TKey, TValue>> dictionaryList)
            : base(_copy(dictionaryList))
        { }
    
        private static IDictionary<TKey, TValue> _copy(IEnumerable<KeyValuePair<TKey, TValue>> dictionaryList)
        {
            var result = new Dictionary<TKey, TValue>();
    
            foreach (KeyValuePair<TKey, TValue> kv in dictionaryList)
                result.Add(kv.Key, kv.Value);
    
            return result;
        }
    }
    /// <summary>
    /// Read only copy of an IList object.
    /// </summary>
    /// <typeparam name="TValue">Data type of the list value.</typeparam>
    public class ReadOnlyListCopy<TValue> : ReadOnlyListWrapper<TValue>
    {
        public ReadOnlyListCopy(IEnumerable<TValue> list)
            : base(new List<TValue>(list))
        { }
    }
    

      

     A caller can then easily make a read-only copy of any dictionary or list by using the example code below:

    var myKeyData = new ReadOnlyDictionaryCopy<string, ExampleData>(myInstance.KeyData);
    var myListData = new ReadOnlyListCopy<ExampleData>(myInstance.ListData);
    

      

    example:

        public class Invoice
        {
            // A unique numerical identifier of an invoice (mandatory)
            public int Id { get; set; }
            // A short description of an invoice (optional).
            public string Description { get; set; }
            // A number of an invoice e.g. 134/10/2018 (mandatory).
            public string Number { get; set; }
            // An issuer of an invoice e.g. Metz-Anderson, 600  Hickman Street,Illinois (mandatory).
            public string Seller { get; set; }
            // A buyer of a service or a product e.g. John Smith, 4285  Deercove Drive, Dallas (mandatory).
            public string Buyer { get; set; }
            // A date when an invoice was issued (mandatory).
            public DateTime CreationDate { get; set; }
            // A date when an invoice was paid (optional).
            public DateTime? AcceptanceDate { get; set; }
            // A collection of invoice items for a given invoice (can be empty but is never null).
            public IList<InvoiceItem> InvoiceItems { get; }
    
            public Invoice()
            {
                InvoiceItems = new List<InvoiceItem>();
            }
        }
    
        public class InvoiceItem
        {
            // A name of an item e.g. eggs.
            public string Name { get; set; }
            // A number of bought items e.g. 10.
            public int Count { get; set; }
            // A price of an item e.g. 20.5.
            public decimal Price { get; set; }
        }
        public interface IInvoiceRepository
        {
    
        }
        public class InvoiceRepository : IInvoiceRepository
        {
    
            protected readonly IQueryable<Invoice> _invoices;
            public InvoiceRepository(IQueryable<Invoice> invoices)
            {
                // Console.WriteLine("Sample debug output");
                if (invoices == null)
                {
                    throw new ArgumentNullException("invoices is null");
                }
                _invoices = invoices;
            }
    
            /// <summary>
            /// Should return a total value of an invoice with a given id. 
            /// If an invoice does not exist null should be returned.
            /// </summary>
            /// <param name="invoiceId"></param>
            /// <returns></returns>
            public decimal? GetTotal(int invoiceId)
            {
                var filteredInvoice = _invoices.Where(_ => _.Id == invoiceId);
                if (!filteredInvoice.Any())
                {
                    return null;
                }
                if (filteredInvoice.Count() > 1)
                {
                    throw new RequestException("invoiceId not unique");
                }
                var defaultInvoice = filteredInvoice.FirstOrDefault();
                if (defaultInvoice.InvoiceItems==null||!defaultInvoice.InvoiceItems.Any())
                {
                    return 0;
                }
                return defaultInvoice.InvoiceItems.Sum(_ => _.Count * _.Price);
            }
    
            /// <summary>
            /// Should return a total value of all unpaid invoices.
            /// </summary>
            /// <returns></returns>
            public decimal GetTotalOfUnpaid()
            {
                if (!_invoices.Any())
                {
                    return 0;
                }
                var filteredInvoice = _invoices.Where(_ => _.AcceptanceDate == null);
                if (!filteredInvoice.Any())
                {
                    return 0;
                }
                var result = 0m;
                foreach(var item in filteredInvoice)
                {
                    if (item.InvoiceItems == null || !item.InvoiceItems.Any()) continue;
                    result += item.InvoiceItems.Sum(_ => _.Count * _.Price);
                }
                return result;
            }
    
            /// <summary>
            /// Should return a dictionary where the name of an invoice item is a key and 
            /// the number of bought items is a value.
            /// 
            /// The number of bought items should be summed 
            /// within a given period of time (from, to). 
            /// Both the from date and the end date can be null.
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <returns></returns>
            public IReadOnlyDictionary<string, long> GetItemsReport(DateTime? from, DateTime? to)
            {
                if (!_invoices.Any())
                {
                    return null;
                }
    
                var filteredInvoice = _invoices;
                if (from != null)
                {
                    filteredInvoice = filteredInvoice.Where(_ => _.CreationDate >= from.Value);
                }
                if (to != null)
                {
                    filteredInvoice = filteredInvoice.Where(_ => _.CreationDate <= to.Value);
                }
                if (!filteredInvoice.Any())
                {
                    return null;
                }
                var listInvoiceItems = new List<InvoiceItem>();
                foreach (var item in filteredInvoice)
                {
                    if (!item.InvoiceItems.Any()) continue;
                    listInvoiceItems.Union(item.InvoiceItems);
                }
                var result = listInvoiceItems
                    .ToLookup(_ => _.Name).ToDictionary(_ => _.Key, _ => (long)(_.Sum(a => a.Count)));
    
                //IReadOnlyDictionary<string, long> testR = result;
                return result;
                    //.ToList(_=>_.Key,_=>(_.Sum(a=>a.Count))).AsEnumerable();
                //result.ToList().AsReadOnly();
                //var readOnlyDinosaurs =
                //new ReadOnlyCollection<string, long>(result);
                //return new ReadOnlyCollection<string, long>(listInvoiceItems.ToLookup(_ => _.Name));
    
            }
        }
    

      

    ReadOnlyCollection or IEnumerable for exposing member collections?

    If you only need to iterate through the collection:

    foreach (Foo f in bar.Foos)
    

    then returning IEnumerable is enough.

    If you need random access to items:

    Foo f = bar.Foos[17];
    

    then wrap it in ReadOnlyCollection.

    IEnumerable to IReadOnlyCollection

    One way would be to construct a list, and call AsReadOnly() on it:

    IReadOnlyCollection<Object> rdOnly = orig.ToList().AsReadOnly();
    

    This produces ReadOnlyCollection<object>, which implements IReadOnlyCollection<Object>.

    Note: Since List<T> implements IReadOnlyCollection<T> as well, the call to AsReadOnly() is optional. Although it is possible to call your method with the result of ToList(), I prefer using AsReadOnly(), so that the readers of my code would see that the method that I am calling has no intention to modify my list. Of course they could find out the same thing by looking at the signature of the method that I am calling, but it is nice to be explicit about it.

    How to work with read-only collections in C#

    Take advantage of read-only generic interfaces such as IReadOnlyList, IReadOnlyDictionary, and IReadOnlyCollection to prevent modifications to collections in your .NET Core applications.

    A collection represents a set of objects used for the storage and retrieval of data. Collections enable you to allocate memory dynamically to store elements and then retrieve them using a key or index as needed.

    You can have standard or generic collections. While standard collections don’t provide type-safety, generic collections are type-safe. The standard collections are part of the System.Collections namespace, and the generic collections are part of the System.Collections.Generic namespace.

    An immutable object is defined as an object that cannot be changed after it has been created. Not all collections are immutable, but you can use the read-only collection types in .NET Core such as IReadOnlyList, IReadOnlyDictionary, and IReadOnlyCollection to implement immutable types. These all are part of the System.Collections.Generic namespace.

    This article discusses these read-only immutable collection types in .NET Core and how you can work with them in C#. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here.

    Read-only collections, dictionaries, and lists in .NET Core

    The IReadOnlyCollection interface extends the IEnumerable interface and represents a basic read-only collection interface. It also includes a Count property apart from the IEnumerable members as shown in the code snippet given below.

    IReadOnlyCollection<Product> data = products;
    int numOfRecords = data.Count;

    The IReadOnlyDictionary interface provides a read-only view of a dictionary and represents a read-only collection of key/value pairs. The following code snippet illustrates how you can define an IReadOnlyDictionary instance.

    public IReadOnlyDictionary<string, string> Dictionary { get; } = new Dictionary<string, string>
            {
                { "1", "ABC" },
                { "2", "XYZ" },
                { "3", "PQR" },
            };

    The IReadOnlyList<T> interface pertaining to the System.Collections.Generic namespace represents a read-only list, i.e., a read-only collection of elements that can be accessed by index.

    Note that the IReadOnlyList interface works similarly to List and in fact can be used in place of List when you want the elements of the list to be read-only. We will illustrate this with an example in the next section.

    Use IReadOnlyList instead of List in C#

    Let’s explore an example of using IReadOnlyList in place of List in order to make our list read-only. Consider the following class:

    public class Author
    {
       public int Id { get; set; }
       public string FirstName { get; set; }
       public string LastName { get; set; }
    }

    Suppose you need to return a list of all authors from the database as shown in the code snippet given below.

    public static List<Author> GetAuthors()
    {
       return new List<Author>
       {
           new Author
           {
               Id = 1,
               FirstName = "Joydip",
               LastName = "Kanjilal"
           },
           new Author
           {
               Id = 2,
               FirstName = "Steve",
               LastName = "Smith"
           }
        };
    }

    For the sake of simplicity, we’re omitting the code necessary to retrieve data (i.e., author records) from the database. The following code snippet shows how you can call the GetAuthors method from the Main method.

    static void Main(string[] args)
    {
        var authors = GetAuthors();
        Console.Read();           
    }

    If you take advantage of IntelliSense in Visual Studio to see the members of the authors list object, you’ll see a list of methods supported by List<Author>. Figure 1 below displays the members of List<Author>. Note that you have a method named Add, so you can easily add authors to the list of authors we’ve created in the preceding code snippet.

    read only collections csharp 01

     

    IDG

    Figure 1.

    The collection used here is clearly mutable in nature. So how do you prevent this data from being changed?

    Here’s where you can take advantage of IReadOnlyList to ensure that the returned data cannot be changed. By changing the return type of the GetAuthors method from List<Author> to IReadOnlyList<Author> we can make our collection read-only.

    The following code snippet illustrates how IReadOnlyList can be used in place of List in the above method.

    public static IReadOnlyList<Author> GetAuthors()
    {
       return new List<Author>
       {
          new Author
          {
              Id = 1,
              FirstName = "Joydip",
              LastName = "Kanjilal"
          },
          new Author
          {
              Id = 2,
              FirstName = "Steve",
              LastName = "Smith"
          }
        };
    }

    Now when you use IntelliSense to inspect IReadOnlyList<Author>, you’ll no longer see Add among the supported methods. Figure 2 below displays the members of the IReadOnlyList<Author> instance.

    read only collections csharp 02

     

    IDG

    Figure 2.

    Create a console application project in Visual Studio

    First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio.

    1. Launch the Visual Studio IDE.
    2. Click on “Create new project.”
    3. In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
    4. Click Next.
    5. In the “Configure your new project” window shown next, specify the name and location for the new project.
    6. Click Create.

    This will create a new .NET Core console application project in Visual Studio 2019. We’ll use this project in the subsequent sections of this article.

    Read-only collections, dictionaries, and lists in .NET Core

    The IReadOnlyCollection interface extends the IEnumerable interface and represents a basic read-only collection interface. It also includes a Count property apart from the IEnumerable members as shown in the code snippet given below.

    RECOMMENDED WHITEPAPERS

    • How to Build the Business Case for Contract Management Solutions

    • Next-Generation Network Embracing the Demands of the Hyper-Connected Enterprise

    • Modernizing Backup in the Cloud

    IReadOnlyCollection<Product> data = products;
    int numOfRecords = data.Count;

    The IReadOnlyDictionary interface provides a read-only view of a dictionary and represents a read-only collection of key/value pairs. The following code snippet illustrates how you can define an IReadOnlyDictionary instance.

    public IReadOnlyDictionary<string, string> Dictionary { get; } = new Dictionary<string, string>
            {
                { "1", "ABC" },
                { "2", "XYZ" },
                { "3", "PQR" },
            };

    The IReadOnlyList<T> interface pertaining to the System.Collections.Generic namespace represents a read-only list, i.e., a read-only collection of elements that can be accessed by index.

    Note that the IReadOnlyList interface works similarly to List and in fact can be used in place of List when you want the elements of the list to be read-only. We will illustrate this with an example in the next section.

    Use IReadOnlyList instead of List in C#

    Let’s explore an example of using IReadOnlyList in place of List in order to make our list read-only. Consider the following class:

    public class Author
    {
       public int Id { get; set; }
       public string FirstName { get; set; }
       public string LastName { get; set; }
    }

    Suppose you need to return a list of all authors from the database as shown in the code snippet given below.

    public static List<Author> GetAuthors()
    {
       return new List<Author>
       {
           new Author
           {
               Id = 1,
               FirstName = "Joydip",
               LastName = "Kanjilal"
           },
           new Author
           {
               Id = 2,
               FirstName = "Steve",
               LastName = "Smith"
           }
        };
    }

    For the sake of simplicity, we’re omitting the code necessary to retrieve data (i.e., author records) from the database. The following code snippet shows how you can call the GetAuthors method from the Main method.

    static void Main(string[] args)
    {
        var authors = GetAuthors();
        Console.Read();           
    }

    If you take advantage of IntelliSense in Visual Studio to see the members of the authors list object, you’ll see a list of methods supported by List<Author>. Figure 1 below displays the members of List<Author>. Note that you have a method named Add, so you can easily add authors to the list of authors we’ve created in the preceding code snippet.

    read only collections csharp 01
     
    IDG

    Figure 1.

    The collection used here is clearly mutable in nature. So how do you prevent this data from being changed?

    Here’s where you can take advantage of IReadOnlyList to ensure that the returned data cannot be changed. By changing the return type of the GetAuthors method from List<Author> to IReadOnlyList<Author> we can make our collection read-only.

    The following code snippet illustrates how IReadOnlyList can be used in place of List in the above method.

    public static IReadOnlyList<Author> GetAuthors()
    {
       return new List<Author>
       {
          new Author
          {
              Id = 1,
              FirstName = "Joydip",
              LastName = "Kanjilal"
          },
          new Author
          {
              Id = 2,
              FirstName = "Steve",
              LastName = "Smith"
          }
        };
    }

    Now when you use IntelliSense to inspect IReadOnlyList<Author>, you’ll no longer see Add among the supported methods. Figure 2 below displays the members of the IReadOnlyList<Author> instance.

    read only collections csharp 02
     
    IDG

    Figure 2.

    Use the IEnumberable interface in C#

    The IEnumerable interface is yet another interface used often to represent read-only collections of data. If you simply want to enumerate the elements of a collection, you can use IEnumerable as shown below.

    public void MyMethod(IEnumerable<Author> authors)
    {
      foreach (Author author in authors)
      {
          //Write your code here
      }
    }

    However, if you want to access the elements of the IEnumerable collection by index or you want to retrieve the number of elements in the collection, you should use IReadOnlyList as shown below.

    public void MyMethod(IReadOnlyList<Author> authors)
    {
      int count = authors.Count;
      for(int index = 0; index < count; index++)
      {
          var author = authors[index];
          //Write your code here
      }
    }

    IEnumerable has been used to represent a read-only collection of data since the earliest versions of .NET. The read-only collection interfaces available in .NET Core and .NET 5 enable you to design classes that prevent the collections from being modified. However, it should be noted that while these read-only collection types provide a read-only view of the data, they are only wrappers. They do not provide an immutable copy of the collection.

    How to do more in C#:

     
  • 相关阅读:
    河南省第十二届大学生程序设计竞赛 F: Information Transmission-1(二分图最大匹配)
    hdu1068:Girls and Boys(最大独立集)
    hdu1068:Girls and Boys(最大独立集)
    hdu1281:棋盘游戏(二分图最大匹配)
    hdu1281:棋盘游戏(二分图最大匹配)
    洛谷P1991 :无线通讯网(最小生成树)
    洛谷P1991 :无线通讯网(最小生成树)
    oGitHub 注册
    Lambda应用设计模式 [转载]
    读取excel数据 到 datagrid 出现 找不到可安装的ISAM
  • 原文地址:https://www.cnblogs.com/panpanwelcome/p/14752018.html
Copyright © 2011-2022 走看看