<PackageReference Include="System.Reactive" Version="6.1.0" />

CompositeDisposable

Represents a group of disposable resources that are disposed together.
using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; namespace System.Reactive.Disposables { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public sealed class CompositeDisposable : ICollection<IDisposable>, IEnumerable<IDisposable>, IEnumerable, ICancelable, IDisposable { [System.Runtime.CompilerServices.Nullable(0)] private sealed class CompositeEnumerator : IEnumerator<IDisposable>, IDisposable, IEnumerator { [System.Runtime.CompilerServices.Nullable(new byte[] { 1, 2 })] private readonly IDisposable[] _disposables; private int _index; public IDisposable Current => _disposables[_index]; object IEnumerator.Current { get { return _disposables[_index]; } } public CompositeEnumerator([System.Runtime.CompilerServices.Nullable(new byte[] { 1, 2 })] IDisposable[] disposables) { _disposables = disposables; _index = -1; } public void Dispose() { IDisposable[] disposables = _disposables; Array.Clear(disposables, 0, disposables.Length); } public bool MoveNext() { IDisposable[] disposables = _disposables; int num; do { num = ++_index; if (num >= disposables.Length) return false; } while (disposables[num] == null); return true; } public void Reset() { _index = -1; } } private readonly object _gate = new object(); private bool _disposed; private object _disposables; private int _count; private const int ShrinkThreshold = 64; private const int MaximumLinearSearchThreshold = 1024; private const int DefaultCapacity = 16; private static readonly CompositeEnumerator EmptyEnumerator = new CompositeEnumerator(Array.Empty<IDisposable>()); public int Count => Volatile.Read(ref _count); public bool IsReadOnly => false; public bool IsDisposed => Volatile.Read(ref _disposed); public CompositeDisposable() { _disposables = new List<IDisposable>(); } public CompositeDisposable(int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException("capacity"); _disposables = new List<IDisposable>(capacity); } public CompositeDisposable(params IDisposable[] disposables) { if (disposables == null) throw new ArgumentNullException("disposables"); (object, int) valueTuple = ToListOrDictionary(disposables); _disposables = valueTuple.Item1; Volatile.Write(ref _count, disposables.Length); } public CompositeDisposable(IEnumerable<IDisposable> disposables) { if (disposables == null) throw new ArgumentNullException("disposables"); (object, int) valueTuple = ToListOrDictionary(disposables); _disposables = valueTuple.Item1; int item = valueTuple.Item2; Volatile.Write(ref _count, item); } [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] private static (object Collection, int Count) ToListOrDictionary(IEnumerable<IDisposable> disposables) { IDisposable[] array = disposables as IDisposable[]; int num; if (array == null) { ICollection<IDisposable> collection = disposables as ICollection<IDisposable>; num = ((collection == null) ? 16 : collection.Count); } else num = array.Length; int num2 = num; if (num2 > 1024) { Dictionary<IDisposable, int> dictionary = new Dictionary<IDisposable, int>(num2); int num3 = 0; foreach (IDisposable disposable in disposables) { if (disposable == null) throw new ArgumentException(Strings_Core.DISPOSABLES_CANT_CONTAIN_NULL, "disposables"); dictionary.TryGetValue(disposable, out int value); dictionary[disposable] = value + 1; num3++; } return (dictionary, num3); } List<IDisposable> list = new List<IDisposable>(num2); foreach (IDisposable disposable2 in disposables) { if (disposable2 == null) throw new ArgumentException(Strings_Core.DISPOSABLES_CANT_CONTAIN_NULL, "disposables"); list.Add(disposable2); } int count = list.Count; return (list, list.Count); } public void Add(IDisposable item) { if (item == null) throw new ArgumentNullException("item"); lock (_gate) { if (!_disposed) { List<IDisposable> list = _disposables as List<IDisposable>; if (list != null) { list.Add(item); if (list.Count > 1024) { Dictionary<IDisposable, int> dictionary = new Dictionary<IDisposable, int>(list.Count + list.Count / 4); foreach (IDisposable item2 in list) { if (item2 != null) { dictionary.TryGetValue(item2, out int value); dictionary[item2] = value + 1; } } _disposables = dictionary; } } else { Dictionary<IDisposable, int> obj = (Dictionary<IDisposable, int>)_disposables; obj.TryGetValue(item, out int value2); obj[item] = value2 + 1; } Volatile.Write(ref _count, _count + 1); return; } } item.Dispose(); } public bool Remove(IDisposable item) { if (item == null) throw new ArgumentNullException("item"); lock (_gate) { if (_disposed) return false; List<IDisposable> list = _disposables as List<IDisposable>; if (list != null) { int num = list.IndexOf(item); if (num < 0) return false; list[num] = null; if (list.Capacity > 64 && _count < list.Capacity / 2) { List<IDisposable> list2 = new List<IDisposable>(list.Capacity / 2); foreach (IDisposable item2 in list) { if (item2 != null) list2.Add(item2); } _disposables = list2; } } else { Dictionary<IDisposable, int> dictionary = (Dictionary<IDisposable, int>)_disposables; if (!dictionary.TryGetValue(item, out int value)) return false; value--; if (value == 0) dictionary.Remove(item); else dictionary[item] = value; } Volatile.Write(ref _count, _count - 1); } item.Dispose(); return true; } public void Dispose() { List<IDisposable> list = null; Dictionary<IDisposable, int> dictionary = null; lock (_gate) { if (!_disposed) { list = (_disposables as List<IDisposable>); dictionary = (_disposables as Dictionary<IDisposable, int>); _disposables = null; Volatile.Write(ref _count, 0); Volatile.Write(ref _disposed, true); } } if (list != null) { foreach (IDisposable item in list) { item?.Dispose(); } } if (dictionary != null) { foreach (KeyValuePair<IDisposable, int> item2 in dictionary) { item2.Key.Dispose(); } } } public void Clear() { IDisposable[] array = default(IDisposable[]); lock (_gate) { if (_disposed) return; object disposables = _disposables; List<IDisposable> list = disposables as List<IDisposable>; if (list != null) { array = list.ToArray(); list.Clear(); } else { Dictionary<IDisposable, int> obj = (Dictionary<IDisposable, int>)disposables; array = new IDisposable[obj.Count]; obj.Keys.CopyTo(array, 0); obj.Clear(); } Volatile.Write(ref _count, 0); } IDisposable[] array2 = array; for (int i = 0; i < array2.Length; i++) { array2[i]?.Dispose(); } } public bool Contains(IDisposable item) { if (item == null) throw new ArgumentNullException("item"); lock (_gate) { if (!_disposed) { object disposables = _disposables; return (disposables as List<IDisposable>)?.Contains(item) ?? ((Dictionary<IDisposable, int>)disposables).ContainsKey(item); } return false; } } public void CopyTo(IDisposable[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); if (arrayIndex < 0 || arrayIndex >= array.Length) throw new ArgumentOutOfRangeException("arrayIndex"); lock (_gate) { if (!_disposed) { if (arrayIndex + _count > array.Length) throw new ArgumentOutOfRangeException("arrayIndex"); int num = arrayIndex; object disposables = _disposables; List<IDisposable> list = disposables as List<IDisposable>; if (list != null) { foreach (IDisposable item in list) { if (item != null) array[num++] = item; } } else { foreach (KeyValuePair<IDisposable, int> item2 in (Dictionary<IDisposable, int>)disposables) { for (int i = 0; i < item2.Value; i++) { array[num++] = item2.Key; } } } } } } public IEnumerator<IDisposable> GetEnumerator() { lock (_gate) { if (!_disposed && _count != 0) { object disposables = _disposables; List<IDisposable> list = disposables as List<IDisposable>; return new CompositeEnumerator((list != null) ? list.ToArray() : ((Dictionary<IDisposable, int>)disposables).Keys.ToArray()); } return EmptyEnumerator; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }