Make a list readonly in c#

2019-06-01 08:47发布

问题:

I have this example code. What I want to do is to make it so that the "Nums" value can only be written to using the "AddNum" method.

namespace ConsoleApplication1
{
    public class Person
    {
        string myName = "N/A";
        int myAge = 0;
        List<int> _nums = new List<int>();

        public List<int> Nums
        {
            get
            {
                return _nums;
            }
        }

        public void AddNum(int NumToAdd)
        {

            _nums.Add(NumToAdd);
        }

        public string Name { get; set; }
        public int Age { get; set; }
    }
}

Somehow, I've tried a bunch of things regarding AsReadOnly() and the readonly keyword, but I can't seem to get it to do what I want it to do.

Here is the sample of the code I have to access the property.

Person p1 = new Person();
p1.Nums.Add(25); //access 1
p1.AddNum(37); //access 2

Console.WriteLine("press any key");
Console.ReadLine();

I really want "access 1" to fail, and "access 2" to be the ONLY way that the value can be set. Thanks in advance for the help.

回答1:

√ DO use ReadOnlyCollection, a subclass of ReadOnlyCollection, or in rare cases IEnumerable for properties or return values representing read-only collections.

The quote from this article.

You should have something like this:

List<int> _nums = new List<int>();

public ReadOnlyCollection<int> Nums
{
    get
    {
        return _nums.AsReadOnly();
    }
}


回答2:

In general, collection types make poor properties because even when a collection is wrapped in ReadOnlyCollection, it's inherently unclear what:

IEnumerable<int> nums = myPerson.Nums;
myPerson.AddNum(23);
foreach(int i in nums) // Should the 23 be included!?
  ...

is supposed to mean. Is the object returned from Nums a snapshot of the numbers that existed when it called, is it a live view?

A cleaner approach is to have a method called something like GetNumsAsArray which returns a new array each time it's called; it may also be helpful in some cases to have a GetNumsAsList variant depending upon what the caller will want to do with the numbers. Some methods only work with arrays, and some only work with lists, so if only one of the above is provided some callers will have to call it and then convert the returned object to the required type.

If performance-sensitive callers will be needing to use this code a lot, it may be helpful to have a more general-purpose method:

int CopyNumsIntoArray(int sourceIndex, int reqCount, ref int[] dest, 
                      int destIndex, CopyCountMode mode);

where CopyCountMode indicates what the code should do the number of items available starting at sourceIndex is greater or less than reqCount; the method should either return the number of items that were available, or throw an exception if it violated the caller's stated expectations. Some callers might start by create and passing in a 10-item array but be prepared to have the method replace it with a bigger array if there are more than ten items to be returned; others might expect that there will be exactly 23 items and be unprepared to handle any other number. Using a parameter to specify the mode will allow one method to service many kinds of callers.

Although many collection authors don't bother including any method that fits the above pattern, such methods can greatly improve efficiency in cases where code wants to work with a significant minority of a collection (e.g. 1,000 items out of a collection of 50,000). In the absence of such methods, code wishing to work with such a range must either ask for a copy of the whole thing (very wasteful) or request thousands of items individually (also wasteful). Allowing the caller to supply the destination array would improve efficiency in the case where the same method makes many queries, especially if the destination array would be large enough to be put on the large object heap.