문제

1st question is about way of protecting my List from changing(remove/add/clear etc...) from outside

there is my way:

class Foo 
{
    public int[] MyCollection
    {
        get{ return (_myCollection==null)?null:_myCollection.ToArray();
    }
    protected List<int> _myCollection;
}

Is it good? Or is there any better ideas, or may be patterns?

2nd: When iam testing this solution with stopwatch, i was very surprised.

List -enumeration was slower than List.ToArray() enumeration with cast time:

List<int> myList = new List<int>();
for (int j = 0; j < 10000; j++)
{
    myList.Add(j);
}
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
    //casting every iteration:
    var ROC = myList.ToArray();
    int count = 0;
    foreach (var a in ROC)
    {
        count += a;
    }
}
sw.Stop();
Console.WriteLine(sw.Elapsed);

It shows me 700 msec, and

List<int> myList = new List<int>();
for (int j = 0; j < 10000; j++)
{
    myList.Add(j);
}
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
    int count = 0;
    //No casting at all
    foreach (var a in myList)
    {
        count += a;
    }
}
sw.Stop();
Console.WriteLine(sw.Elapsed);

Shows me 843 msec... why is it so?

도움이 되었습니까?

해결책 2

For read only collection you could use List<T>.AsReadOnly()

public IList<int> MyCollection
{
   get{ return _myCollection==null  ? null : _myCollection.AsReadOnly();
}

If necessary and in order to make more clear that we're talking about a read only collection you could define your property like this

public IReadOnlyList<int> MyCollection

다른 팁

First off, if you have two questions then post two questions. I'll answer your first question.

Second, good for you for thinking about how to protect your list from mutation. A lot of people forget about this and expose their internal state to callers.

There are a number of things you can do.

1) Don't use a mutable list in the first place. Use an immutable list. The Add method of an immutable list returns a different list rather than mutating the current list. There are new immutable list classes in the BCL now. These can be very efficient in both space and time.

2) Use a mutable list and make a copy every time. You're already doing this. The problem is of course that it is slow and uses a lot of memory.

3) Return AsReadOnly as Claudio suggests. Note that this just makes a read-only façade around the list; if the list changes, the read-only façade changes too. "Read only" means just that: the user cannot write it. It does not mean that it never changes. Also, there's an enumeration issue, which I'll address in (4):

4) Make LINQ do the work. return _myCollection.Select(x=>x); and have the property be of type IEnumerable<int>. This has two downsides. First, the caller only gets forward enumeration. Second, suppose the caller is doing a foreach over the property, and then inside the loop does something which changes the list. This will cause an exception; you can't change a list while you're enumerating it. If you need to support that scenario then (1) or (2) are your best bet; in those the enumeration will be over a snapshot, not over the mutating list.

Answer to the Second Question in the Question

Iterating over an int array vs iterating over a list of integers produces very different outputs at the IL code level.

Iterating over an Integer Array

  IL_0015:  ldloc.3
  IL_0016:  ldloc.s    CS$7$0001
  IL_0018:  ldelem.i4
  IL_0019:  stloc.2
  IL_001a:  ldloc.1
  IL_001b:  ldloc.2
  IL_001c:  add
  IL_001d:  stloc.1
  IL_001e:  ldloc.s    CS$7$0001
  IL_0020:  ldc.i4.1
  IL_0021:  add
  IL_0022:  stloc.s    CS$7$0001
  IL_0024:  ldloc.s    CS$7$0001
  IL_0026:  ldloc.3
  IL_0027:  ldlen
  IL_0028:  conv.i4
  IL_0029:  blt.s      IL_0015

Iterating over an Integer List

IL_0010:  ldloca.s   CS$5$0000
IL_0012:  call       instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
IL_0017:  stloc.1
IL_0018:  ldloc.0
IL_0019:  ldloc.1
IL_001a:  add
IL_001b:  stloc.0
IL_001c:  ldloca.s   CS$5$0000
IL_001e:  call       instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
IL_0023:  brtrue.s   IL_0010

The key here is that when using a list, the CLR is performing a type each iteration ( get_Current() object type to int). This may be causing performance issues.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top