3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>Microsoft</OWNER>
9 namespace System.Collections.ObjectModel
12 using System.Collections;
13 using System.Collections.Generic;
14 using System.Diagnostics;
18 [System.Runtime.InteropServices.ComVisible(false)]
19 [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
20 [DebuggerDisplay("Count = {Count}")]
21 public class Collection<T>: IList<T>, IList, IReadOnlyList<T>
25 private Object _syncRoot;
28 items = new List<T>();
31 public Collection(IList<T> list) {
33 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
39 get { return items.Count; }
42 protected IList<T> Items {
46 public T this[int index] {
47 get { return items[index]; }
49 if( items.IsReadOnly) {
50 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
53 if (index < 0 || index >= items.Count) {
54 ThrowHelper.ThrowArgumentOutOfRangeException();
57 SetItem(index, value);
61 public void Add(T item) {
62 if( items.IsReadOnly) {
63 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
66 int index = items.Count;
67 InsertItem(index, item);
71 if( items.IsReadOnly) {
72 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
78 public void CopyTo(T[] array, int index) {
79 items.CopyTo(array, index);
82 public bool Contains(T item) {
83 return items.Contains(item);
86 public IEnumerator<T> GetEnumerator() {
87 return items.GetEnumerator();
90 public int IndexOf(T item) {
91 return items.IndexOf(item);
94 public void Insert(int index, T item) {
95 if (items.IsReadOnly) {
96 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
99 if (index < 0 || index > items.Count) {
100 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
103 InsertItem(index, item);
106 public bool Remove(T item) {
107 if( items.IsReadOnly) {
108 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
111 int index = items.IndexOf(item);
112 if (index < 0) return false;
117 public void RemoveAt(int index) {
118 if( items.IsReadOnly) {
119 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
122 if (index < 0 || index >= items.Count) {
123 ThrowHelper.ThrowArgumentOutOfRangeException();
129 protected virtual void ClearItems() {
133 protected virtual void InsertItem(int index, T item) {
134 items.Insert(index, item);
137 protected virtual void RemoveItem(int index) {
138 items.RemoveAt(index);
141 protected virtual void SetItem(int index, T item) {
145 bool ICollection<T>.IsReadOnly {
147 return items.IsReadOnly;
151 IEnumerator IEnumerable.GetEnumerator() {
152 return ((IEnumerable)items).GetEnumerator();
155 bool ICollection.IsSynchronized {
156 get { return false; }
159 object ICollection.SyncRoot {
161 if( _syncRoot == null) {
162 ICollection c = items as ICollection;
164 _syncRoot = c.SyncRoot;
167 System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
174 void ICollection.CopyTo(Array array, int index) {
176 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
179 if (array.Rank != 1) {
180 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
183 if( array.GetLowerBound(0) != 0 ) {
184 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
188 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
191 if (array.Length - index < Count) {
192 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
195 T[] tArray = array as T[];
196 if (tArray != null) {
197 items.CopyTo(tArray , index);
201 // Catch the obvious case assignment will fail.
202 // We can found all possible problems by doing the check though.
203 // For example, if the element type of the Array is derived from T,
204 // we can't figure out if we can successfully copy the element beforehand.
206 Type targetType = array.GetType().GetElementType();
207 Type sourceType = typeof(T);
208 if(!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) {
209 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
213 // We can't cast array of value type to object[], so we don't support
214 // widening of primitive types here.
216 object[] objects = array as object[];
217 if( objects == null) {
218 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
221 int count = items.Count;
223 for (int i = 0; i < count; i++) {
224 objects[index++] = items[i];
227 catch(ArrayTypeMismatchException) {
228 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
233 object IList.this[int index] {
234 get { return items[index]; }
236 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
239 this[index] = (T)value;
241 catch (InvalidCastException) {
242 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
248 bool IList.IsReadOnly {
250 return items.IsReadOnly;
254 bool IList.IsFixedSize {
256 // There is no IList<T>.IsFixedSize, so we must assume that only
257 // readonly collections are fixed size, if our internal item
258 // collection does not implement IList. Note that Array implements
259 // IList, and therefore T[] and U[] will be fixed-size.
260 IList list = items as IList;
263 return list.IsFixedSize;
265 return items.IsReadOnly;
269 int IList.Add(object value) {
270 if( items.IsReadOnly) {
271 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
273 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
278 catch (InvalidCastException) {
279 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
282 return this.Count - 1;
285 bool IList.Contains(object value) {
286 if(IsCompatibleObject(value)) {
287 return Contains((T) value);
292 int IList.IndexOf(object value) {
293 if(IsCompatibleObject(value)) {
294 return IndexOf((T)value);
299 void IList.Insert(int index, object value) {
300 if( items.IsReadOnly) {
301 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
303 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
306 Insert(index, (T)value);
308 catch (InvalidCastException) {
309 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
314 void IList.Remove(object value) {
315 if( items.IsReadOnly) {
316 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
319 if(IsCompatibleObject(value)) {
324 private static bool IsCompatibleObject(object value) {
325 // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>.
326 // Note that default(T) is not equal to null for value types except when T is Nullable<U>.
327 return ((value is T) || (value == null && default(T) == null));