A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Collections;
6using System.Collections.Generic;
7using JetBrains.Annotations;
8
9namespace osu.Framework.Lists
10{
11 /// <summary>
12 /// A wrapper for an array that provides notifications when elements are changed.
13 /// </summary>
14 /// <typeparam name="T">The type of elements stored in the array.</typeparam>
15 public class ObservableArray<T> : IReadOnlyList<T>, IEquatable<ObservableArray<T>>, INotifyArrayChanged
16 {
17 /// <summary>
18 /// Invoked when an element of the array is changed via <see cref="this[int]"/>.
19 /// </summary>
20 public event Action ArrayElementChanged;
21
22 [NotNull]
23 private readonly T[] wrappedArray;
24
25 public ObservableArray(T[] arrayToWrap)
26 {
27 wrappedArray = arrayToWrap ?? throw new ArgumentNullException(nameof(arrayToWrap));
28 }
29
30 public IEnumerator<T> GetEnumerator()
31 {
32 return ((IEnumerable<T>)wrappedArray).GetEnumerator();
33 }
34
35 IEnumerator IEnumerable.GetEnumerator()
36 {
37 return wrappedArray.GetEnumerator();
38 }
39
40 public bool Equals(ObservableArray<T> other)
41 {
42 if (ReferenceEquals(null, other)) return false;
43 if (ReferenceEquals(this, other)) return true;
44
45 return wrappedArray == other.wrappedArray;
46 }
47
48 public override bool Equals(object obj)
49 {
50 if (ReferenceEquals(null, obj)) return false;
51 if (ReferenceEquals(this, obj)) return true;
52
53 return obj.GetType() == GetType() && Equals((ObservableArray<T>)obj);
54 }
55
56 public override int GetHashCode()
57 {
58 return HashCode.Combine(wrappedArray);
59 }
60
61 public int Count => wrappedArray.Length;
62
63 public T this[int index]
64 {
65 get => wrappedArray[index];
66 set
67 {
68 if (EqualityComparer<T>.Default.Equals(wrappedArray[index], value))
69 return;
70
71 var previousValue = wrappedArray[index];
72 if (previousValue is INotifyArrayChanged previousNotifier)
73 previousNotifier.ArrayElementChanged -= OnArrayElementChanged;
74
75 wrappedArray[index] = value;
76 if (value is INotifyArrayChanged notifier)
77 notifier.ArrayElementChanged += OnArrayElementChanged;
78
79 OnArrayElementChanged();
80 }
81 }
82
83 protected void OnArrayElementChanged()
84 {
85 ArrayElementChanged?.Invoke();
86 }
87
88 public static implicit operator ObservableArray<T>(T[] source) => source == null ? null : new ObservableArray<T>(source);
89 }
90}