9月 29

之前学习了List,Hashtable,Stack,Queue的framework内部实现

最后发现都存在一个数组,数组实现的,追根溯源,数组Array又是怎么实现的呢,今天研究一下.

源代码位于

http://referencesource.microsoft.com/#mscorlib/system/array.cs

written by ocean

9月 28

栈 Stack 最大特点:先进后出

代码地址

http://referencesource.microsoft.com/#mscorlib/system/collections/stack.cs

Stack,栈,内部实现也是依靠数组

        private Object[] _array;     // Storage for stack elements 
        private int _size;           // Number of items in the stack.
        private int _version;        // Used to keep enumerator in [....] w/ collection. 
        private const int _defaultCapacity = 10; 
        public Stack() {
            _array = new Object[_defaultCapacity];
            _size = 0;
            _version = 0;
        }

_size既是栈内元素的容量,又是一个指针,指向当前栈的最新元素.

Push,压栈方法

        // Pushes an item to the top of the stack.
        // 
        public virtual void Push(Object obj) {
            //Contract.Ensures(Count == Contract.OldValue(Count) + 1);
            if (_size == _array.Length) {
                Object[] newArray = new Object[2*_array.Length];
                Array.Copy(_array, 0, newArray, 0, _size);
                _array = newArray;
            }
            _array[_size++] = obj;
            _version++;
        }

将新加入元素放到栈顶,指针同时+1

另外数组扩容也是按照2倍来的

Pop,出栈方法

        // Pops an item from the top of the stack.  If the stack is empty, Pop
        // throws an InvalidOperationException.
        public virtual Object Pop() {
            if (_size == 0)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyStack"));
            //Contract.Ensures(Count == Contract.OldValue(Count) - 1);
            Contract.EndContractBlock();
            _version++;
            Object obj = _array[--_size];
            _array[_size] = null;     // Free memory quicker.
            return obj;
        }

返回当前栈顶的最后一个元素

栈顶指针减减.

把栈顶指针元素清空

Contains方法

        public virtual bool Contains(Object obj) {
            int count = _size; 
            while (count-- > 0) {
                if (obj == null) {
                    if (_array[count] == null)
                        return true;
                }
                else if (_array[count] != null && _array[count].Equals(obj)) {
                    return true;
                }
            }
            return false;
        }

依然使用的是遍历方式,注意效率

其内部封装了SyncStack的线程安全类

可以使用Synchronized方法调用

        // Returns a synchronized Stack.
        //
        [HostProtection(Synchronization=true)]
        public static Stack Synchronized(Stack stack) {
            if (stack==null)
                throw new ArgumentNullException("stack");
            Contract.Ensures(Contract.Result<Stack>() != null);
            Contract.EndContractBlock();
            return new SyncStack(stack);
        }

其他也没什么特别的了

接下来是Queue 队列,最大特点,先进先出

源代码地址

http://referencesource.microsoft.com/#mscorlib/system/collections/queue.cs

哈哈,依然是数组方式实现,看来接下来要学习一下数组了.

注意这四个变量

        private Object[] _array;
        private int _head;       // First valid element in the queue
        private int _tail;       // Last valid element in the queue
        private int _size;       // Number of elements.

_head是队列头,_tail是队列尾._size则是队列的长度.整个队列被看成是一个圆圈.

Enqueue方法,加入队列

        // Adds obj to the tail of the queue.
        //
        public virtual void Enqueue(Object obj) {
            if (_size == _array.Length) {
                int newcapacity = (int)((long)_array.Length * (long)_growFactor / 100);
                if (newcapacity < _array.Length + _MinimumGrow) {
                    newcapacity = _array.Length + _MinimumGrow;
                }
                SetCapacity(newcapacity);
            } 
            _array[_tail] = obj;
            _tail = (_tail + 1) % _array.Length;
            _size++;
            _version++;
        }

元素被直接加到队列尾巴_tail位置上,尾巴指针+1,整个队列元素数量+1

Dequeue方法,出队列

        public virtual Object Dequeue() {
            if (Count == 0)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyQueue"));
            Contract.EndContractBlock(); 
            Object removed = _array[_head];
            _array[_head] = null;
            _head = (_head + 1) % _array.Length;
            _size--;
            _version++;
            return removed;
        }

从队列头拿出元素,队列头指针向后移动一位.

SetCapacity方法

用来刷新队列容量,同时存在一个既有数据重新放置的问题

        // PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity
        // must be >= _size.
        private void SetCapacity(int capacity) {
            Object[] newarray = new Object[capacity];
            if (_size > 0) {
                if (_head < _tail) {
                    Array.Copy(_array, _head, newarray, 0, _size);
                } else {
                    Array.Copy(_array, _head, newarray, 0, _array.Length - _head);
                    Array.Copy(_array, 0, newarray, _array.Length - _head, _tail);
                }
            } 
            _array = newarray;
            _head = 0;
            _tail = (_size == capacity) ? 0 : _size;            
            _version++;
        }

分为_head < _tail 和 _head >= _tail两种情况,这个很好理解了.

最后,

依然有线程安全的队列 SynchronizedQueue 类

通过方法Synchronized调用

        public static Queue Synchronized(Queue queue)
        {
            if (queue==null)
                throw new ArgumentNullException("queue");
            Contract.EndContractBlock();
            return new SynchronizedQueue(queue);
        }

written by ocean

9月 24

昨天看了List的内部实现,发现List很多方法都是遍历寻找,如果数据量大的话,效率肯定很低.

一般这种都会用哈希表,今天迫不及待的想学习一下哈希表的内部是怎么实现的

Assembly:mscorlib.dll

Namespace:System.Collections

源代码

http://referencesource.microsoft.com/#mscorlib/system/collections/hashtable.cs

Hashtable的初始化方法非常多,找一个最典型的,并且我去掉了一些校验性非核心性的一些代码

		public Hashtable(int capacity, float loadFactor) { 
            // Based on perf work, .72 is the optimal load factor for this table.  
            this.loadFactor = 0.72f * loadFactor; 
            double rawsize = capacity / this.loadFactor; 
            // Avoid awfully small sizes
            int hashsize = (rawsize > InitialSize)  

            loadsize = (int)(this.loadFactor * hashsize);
            isWriterInProgress = false; 
        }

毋庸置疑,Hashtable内部也是用数组实现的,0.72的填充因素

        // The total number of entries in the hash table.
        private  int count;
	// The total number of collision bits set in the hashtable
        private int occupancy;
	//当前状态可以储存的数据数量
	private  int loadsize;

从这几个变量可以看出来,Hashtable是数组,但是数组未填充满,留有一定的碰撞空间

        // The hash table data.
        // This cannot be serialised
        private struct bucket {
            public Object key;
            public Object val;
            public int hash_coll;   // Store hash code; sign bit means there was a collision.
        } 
        private bucket[] buckets;

这个就是数组存储的数据结构了,只有3个字段,使用了Struct,

key,val,顾名思义.hash_coll则记录了Key的hash值和冲突数据.

在看Add和Get方法之前,先熟悉几个函数

        private uint InitHash(Object key, int hashsize, out uint seed, out uint incr) {
            // Hashcode must be positive.  Also, we must not use the sign bit, since
            // that is used for the collision bit.
            uint hashcode = (uint) GetHash(key) & 0x7FFFFFFF;
            seed = (uint) hashcode;
            // Restriction: incr MUST be between 1 and hashsize - 1, inclusive for
            // the modular arithmetic to work correctly.  This guarantees you'll
            // visit every bucket in the table exactly once within hashsize 
            // iterations.  Violate this and it'll cause obscure bugs forever.
            // If you change this calculation for h2(key), update putEntry too!
            incr = (uint)(1 + ((seed * HashPrime) % ((uint)hashsize - 1)));
            return hashcode;
        }

InitHash用来取hashCode,只取正值

        private void expand()  {
            int rawsize = HashHelpers.ExpandPrime(buckets.Length);
            rehash(rawsize, false);
        }

expand是扩充Hash数组用的,和List一样,也是每次扩大2倍

        private void rehash( int newsize, bool forceNewHashCode ) {

            // reset occupancy
            occupancy=0;
        
            // Don't replace any internal state until we've finished adding to the 
            // new bucket[].  This serves two purposes: 
            //   1) Allow concurrent readers to see valid hashtable contents 
            //      at all times
            //   2) Protect against an OutOfMemoryException while allocating this 
            //      new bucket[].
            bucket[] newBuckets = new bucket[newsize];
    
            // rehash table into new buckets
            int nb;
            for (nb = 0; nb < buckets.Length; nb++){
                bucket oldb = buckets[nb];
                if ((oldb.key != null) && (oldb.key != buckets)) {
                    int hashcode = ((forceNewHashCode ? GetHash(oldb.key) : oldb.hash_coll) & 0x7FFFFFFF);                              
                    putEntry(newBuckets, oldb.key, oldb.val, hashcode);
                }
            } 
            // New bucket[] is good to go - replace buckets and other internal state.
#if !FEATURE_CORECLR
            Thread.BeginCriticalRegion();            
#endif
            isWriterInProgress = true;
            buckets = newBuckets;
            loadsize = (int)(loadFactor * newsize);
            UpdateVersion();
            isWriterInProgress = false;
#if !FEATURE_CORECLR            
            Thread.EndCriticalRegion();   
#endif         
            // minimun size of hashtable is 3 now and maximum loadFactor is 0.72 now.
            Contract.Assert(loadsize < newsize, "Our current implementaion means this is not possible.");
            return;
        }

rehash,会把hash已有的数据重新散列一次,主要应对hash容器扩大之后的数据位置变迁

Add方法

        public virtual void Add(Object key, Object value) {
            Insert(key, value, true);
        }

实际调用内部Insert方法实现,true参数表示如果Key相同则抛出异常

Insert函数实现相对复杂,这里记一下大概实现思路

首先算key的hashcode,然后算散列值存入数组,如果发现数组已经被占用,则重新算散列值,如果也被占用,则再次计算.

直到找到没有被占用可以存入的位置为止.

Get方法

根据散列值直接数组索引取,如果碰撞就再次计算再次取,直到取到为止.

摘抄一部分代码,MS的源码里面的注释都很清楚啊,只可惜是E文的,看起来有点不爽

                bucket b;
                int bucketNumber = (int) (seed % (uint)lbuckets.Length);                
                do
                {
                    int currentversion;

                    //     A read operation on hashtable has three steps:
                    //        (1) calculate the hash and find the slot number.
                    //        (2) compare the hashcode, if equal, go to step 3. Otherwise end.
                    //        (3) compare the key, if equal, go to step 4. Otherwise end.
                    //        (4) return the value contained in the bucket.
                    //     After step 3 and before step 4. A writer can kick in a remove the old item and add a new one 
                    //     in the same bukcet. So in the reader we need to check if the hash table is modified during above steps.
                    //
                    // Writers (Insert, Remove, Clear) will set 'isWriterInProgress' flag before it starts modifying 
                    // the hashtable and will ckear the flag when it is done.  When the flag is cleared, the 'version'
                    // will be increased.  We will repeat the reading if a writer is in progress or done with the modification 
                    // during the read.
                    //
                    // Our memory model guarantee if we pick up the change in bucket from another processor, 
                    // we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
                    //                    
                    int spinCount = 0;
                    do {
                        // this is violate read, following memory accesses can not be moved ahead of it.
                        currentversion = version;
                        b = lbuckets[bucketNumber];                        

                        // The contention between reader and writer shouldn't happen frequently.
                        // But just in case this will burn CPU, yield the control of CPU if we spinned a few times.
                        // 8 is just a random number I pick. 
                        if( (++spinCount) % 8 == 0 ) {   
                            Thread.Sleep(1);   // 1 means we are yeilding control to all threads, including low-priority ones.
                        }
                    } while ( isWriterInProgress || (currentversion != version) );

                    if (b.key == null) {
                        return null;
                    }
                    if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && 
                        KeyEquals (b.key, key))
                        return b.val;
                    bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);                                  
                } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
                return null;

防止并发读取脏数据这一段最有意思

                        if( (++spinCount) % 8 == 0 ) {   
                            Thread.Sleep(1);   // 1 means we are yeilding control to all threads, including low-priority ones.
                        }

ContainsKey方法

实际就是get方法类似,只是去掉了防脏读的逻辑

值得一提的是ContainsValue方法

该方法用的是遍历的方法,So,效率应该就会低了.

        // Checks if this hashtable contains an entry with the given value. The
        // values of the entries of the hashtable are compared to the given value
        // using the Object.Equals method. This method performs a linear
        // search and is thus be substantially slower than the ContainsKey
        // method.
        // 
        public virtual bool ContainsValue(Object value) {

            if (value == null) {
                for (int i = buckets.Length; --i >= 0;) {
                    if (buckets[i].key != null && buckets[i].key != buckets && buckets[i].val == null)
                        return true;
                }
            }
            else {
                for (int i = buckets.Length; --i >= 0;) {
                  Object val = buckets[i].val;
                  if (val!=null && val.Equals(value)) return true;
                }
            }
            return false;
        }

最后还有一个重要方法Remove

        public virtual void Remove(Object key) {
            if (key == null) {
                throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
            }
            Contract.EndContractBlock();
            Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously!  Don't do that - use Hashtable.Synchronized.");

            uint seed;
            uint incr;
            // Assuming only one concurrent writer, write directly into buckets.
            uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
            int ntry = 0;
            
            bucket b;
            int bn = (int) (seed % (uint)buckets.Length);  // bucketNumber
            do {
                b = buckets[bn];
                if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && 
                    KeyEquals (b.key, key)) {
#if !FEATURE_CORECLR
                    Thread.BeginCriticalRegion();    
#endif                
                    isWriterInProgress = true;
                    // Clear hash_coll field, then key, then value
                    buckets[bn].hash_coll &= unchecked((int)0x80000000);
                    if (buckets[bn].hash_coll != 0) {
                        buckets[bn].key = buckets;
                    } 
                    else {
                        buckets[bn].key = null;
                    }
                    buckets[bn].val = null;  // Free object references sooner & simplify ContainsValue.
                    count--;
                    UpdateVersion();
                    isWriterInProgress = false; 
#if !FEATURE_CORECLR                   
                    Thread.EndCriticalRegion();   
#endif                 
                    return;
                }
                bn = (int) (((long)bn + incr)% (uint)buckets.Length);                               
            } while (b.hash_coll < 0 && ++ntry < buckets.Length);

            //throw new ArgumentException(Environment.GetResourceString("Arg_RemoveArgNotFound"));
        }

也是计算散列值,直接将Key和Value置为null

注意这一句

    if (buckets[bn].hash_coll != 0) {
        buckets[bn].key = buckets;
    }

这个地方key值没有置为null,很有意思

是为这个位置碰撞后,后面的数据留的,否则为null就返回false了

written by ocean

9月 23

说起来惭愧,入行.NET开发也有六七年了,居然没有去认真研究过内部实现机制.很多特性,技巧也都是只听听介绍就直接用了.至于为什么,实现方式等等,也都只是了解一下.

今天开始学习一下,写个系列.

List,这个是使用最频繁的一个类了,今天就从List开始了

源代码地址

http://referencesource.microsoft.com/#mscorlib/system/collections/Generic/list.cs

首先看一下定义

public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>

实现了三个接口,主要是List

    public interface IList<T> : ICollection<T>
    {
        // The Item property provides methods to read and edit entries in the List.
        T this[int index] {
            get;
            set;
        } 
        // Returns the index of a particular item, if it is in the list.
        // Returns -1 if the item isn't in the list.
        int IndexOf(T item);
    
        // Inserts value into the list at position index.
        // index must be non-negative and less than or equal to the 
        // number of elements in the list.  If index equals the number
        // of items in the list, then value is appended to the end.
        void Insert(int index, T item);
        
        // Removes the item at position index.
        void RemoveAt(int index);
    }
    public interface ICollection<T> : IEnumerable<T>
    {
        // Number of items in the collections.        
        int Count { get; } 
        bool IsReadOnly { get; } 
        void Add(T item); 
        void Clear(); 
        bool Contains(T item);  
        // CopyTo copies a collection into an Array, starting at a particular
        // index into the array.
        // 
        void CopyTo(T[] array, int arrayIndex); 
        //void CopyTo(int sourceIndex, T[] destinationArray, int destinationIndex, int count); 
        bool Remove(T item);
    }

初始化方法一,内部果然是一个数组,另外做了一个_emptyArray,减少new

        private T[] _items;
        static readonly T[]  _emptyArray = new T[0];     
        public List() {
            _items = _emptyArray;
        }

初始化方法二,直接定义数组长度

        public List(int capacity) {
            if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            Contract.EndContractBlock(); 
            if (capacity == 0)
                _items = _emptyArray;
            else
                _items = new T[capacity];
        }

初始化方法三,直接给定集合

        // Constructs a List, copying the contents of the given collection. The
        // size and capacity of the new list will both be equal to the size of the
        // given collection.
        // 
        public List(IEnumerable<T> collection) {
            if (collection==null)
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
            Contract.EndContractBlock(); 
            ICollection<T> c = collection as ICollection<T>;
            if( c != null) {
                int count = c.Count;
                if (count == 0)
                {
                    _items = _emptyArray;
                }
                else {
                    _items = new T[count];
                    c.CopyTo(_items, 0);
                    _size = count;
                }
            }    
            else {                
                _size = 0;
                _items = _emptyArray;
                // This enumerable could be empty.  Let Add allocate a new array, if needed.
                // Note it will also go to _defaultCapacity first, not 1, then 2, etc. 
                using(IEnumerator<T> en = collection.GetEnumerator()) {
                    while(en.MoveNext()) {
                        Add(en.Current);                                    
                    }
                }
            }
        }

里面都写了注释,很好很强大 

学习一下最常用的Add方法

        private int _size;
        public void Add(T item) {
            if (_size == _items.Length) EnsureCapacity(_size + 1);
            _items[_size++] = item;
            _version++;
        }
        private void EnsureCapacity(int min) {
            if (_items.Length < min) {
                int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
                // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
                // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
                if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
                if (newCapacity < min) newCapacity = min;
                Capacity = newCapacity;
            }
        }

在这里,_size变量记录了当前数组使用的长度,如果超过已经定义数组的长度,则会翻倍.

所以,如果是一个很大的基本增加元素的数组,初始定义好数组长度比较好.

RemoveAt方法

        public void RemoveAt(int index) {
            if ((uint)index >= (uint)_size) {
                ThrowHelper.ThrowArgumentOutOfRangeException();
            }
            Contract.EndContractBlock();
            _size--;
            if (index < _size) {
                Array.Copy(_items, index + 1, _items, index, _size - index);
            }
            _items[_size] = default(T);
            _version++;
        }

Copy方法会把所有index后面的元素超前移动一位,所以如果整个数组很大,RemoveAt的开销就很可观了

Insert方法

        // Inserts an element into this list at a given index. The size of the list
        // is increased by one. If required, the capacity of the list is doubled
        // before inserting the new element.
        // 
        public void Insert(int index, T item) {
            // Note that insertions at the end are legal.
            if ((uint) index > (uint)_size) {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
            }
            Contract.EndContractBlock();
            if (_size == _items.Length) EnsureCapacity(_size + 1);
            if (index < _size) {
                Array.Copy(_items, index, _items, index + 1, _size - index);
            }
            _items[index] = item;
            _size++;            
            _version++;
        }

类似于Add和remove的组合,

因为有可能会数组容量翻倍,顺序是先把所有Index之后的元素往后移一位,然后index塞入item

方法中有一个变量_version.

        private int _version;

这个变量在增加删除移动的时候会改变值,代表版本号

        public void ForEach(Action<T> action) { 
            int version = _version; 
            for(int i = 0 ; i < _size; i++) {
                if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
                    break;
                }
                action(_items[i]);
            } 
            if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
        }

在foreach的时候之前会记录版本号,之后会比较,所以当我们foreach的时候改变item值会报错

soga.

        public int IndexOf(T item)
        public int LastIndexOf(T item) 
        public T Find(Predicate<T> match)
        public T FindLast(Predicate<T> match)
        public List<T> FindAll(Predicate<T> match)

这几个方法都会遍历数组,Last是从后面开始遍历,So,如果数组很大的话还是Hash来的快

索引器

        public T this[int index] {
#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
            get {
                // Following trick can reduce the range check by one
                if ((uint) index >= (uint)_size) {
                    ThrowHelper.ThrowArgumentOutOfRangeException();
                }
                Contract.EndContractBlock();
                return _items[index]; 
            }

#if !FEATURE_CORECLR
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
            set {
                if ((uint) index >= (uint)_size) {
                    ThrowHelper.ThrowArgumentOutOfRangeException();
                }
                Contract.EndContractBlock();
                _items[index] = value;
                _version++;
            }
        }

典型的数组啊.这块也解释了为什么for循环改变item值不会报错

Contains方法,依然是遍历整个数组

        public bool Contains(T item) {
            if ((Object) item == null) {
                for(int i=0; i<_size; i++)
                    if ((Object) _items[i] == null)
                        return true;
                return false;
            }
            else {
                EqualityComparer<T> c = EqualityComparer<T>.Default;
                for(int i=0; i<_size; i++) {
                    if (c.Equals(_items[i], item)) return true;
                }
                return false;
            }
        }

另外代码里发现了一个好玩的东西

        // Is this List synchronized (thread-safe)?
        bool System.Collections.ICollection.IsSynchronized {
            get { return false; }
        }

直接返回false,说明List不是线程安全的类

但是内部类定义了SynchronizedList

        internal static IList<T> Synchronized(List<T> list) {
            return new SynchronizedList(list);
        }

        [Serializable()]
        internal class SynchronizedList : IList<T> {
            private List<T> _list;
            private Object _root;
    
            internal SynchronizedList(List<T> list) {
                _list = list;
                _root = ((System.Collections.ICollection)list).SyncRoot;
            }

            public int Count {
                get {
                    lock (_root) { 
                        return _list.Count; 
                    }
                }
            }

            public bool IsReadOnly {
                get {
                    return ((ICollection<T>)_list).IsReadOnly;
                }
            }

            public void Add(T item) {
                lock (_root) { 
                    _list.Add(item); 
                }
            }

            public void Clear() {
                lock (_root) { 
                    _list.Clear(); 
                }
            }

            public bool Contains(T item) {
                lock (_root) { 
                    return _list.Contains(item);
                }
            }

            public void CopyTo(T[] array, int arrayIndex) {
                lock (_root) { 
                    _list.CopyTo(array, arrayIndex);
                }
            }

            public bool Remove(T item) {
                lock (_root) { 
                    return _list.Remove(item);
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
                lock (_root) { 
                    return _list.GetEnumerator();
                }
            }

            IEnumerator<T> IEnumerable<T>.GetEnumerator() {
                lock (_root) { 
                    return ((IEnumerable<T>)_list).GetEnumerator();
                }
            }

            public T this[int index] {
                get {
                    lock(_root) {
                        return _list[index];
                    }
                }
                set {
                    lock(_root) {
                        _list[index] = value;
                    }
                }
            }

            public int IndexOf(T item) {
                lock (_root) {
                    return _list.IndexOf(item);
                }
            }

            public void Insert(int index, T item) {
                lock (_root) {
                    _list.Insert(index, item);
                }
            }

            public void RemoveAt(int index) {
                lock (_root) {
                    _list.RemoveAt(index);
                }
            }
        }

恩,这个不知道微软怎么想的,只允许自己用线程安全的List,不允许外面调用,哈哈

written by ocean