using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Developpez.Dotnet.Collections { /// /// Représente une liste immuable d'objets. Toutes les méthodes qui modifient la liste /// renvoient une copie modifiée. /// /// Type des éléments de la liste public class ImmutableList : IList, IEquatable> { #region Private data private readonly IList _items; private readonly int _hashCode; #endregion #region Constructor /// /// Initialise une nouvelle instance de /// /// Les éléments de la liste public ImmutableList(IEnumerable items) { _items = items.ToArray(); _hashCode = ComputeHash(); } #endregion #region Public members /// /// Renvoie une copie de la liste en y ajoutant l'élément spécifié /// /// Elément à ajouter /// La nouvelle liste public ImmutableList Add(T item) { return this .Append(item) .AsImmutable(); } /// /// Renvoie une copie de la liste en en retirant l'élément spécifié /// /// Elément à retirer /// La nouvelle liste public ImmutableList Remove(T item) { return this .SkipFirst(it => it.SafeEquals(item)) .AsImmutable(); } /// /// Renvoie une copie de la liste en y insérant l'élément spécifié à la position spécifiée /// /// Position à laquelle insérer l'élément /// Elément à insérer /// La nouvelle liste public ImmutableList Insert(int index, T item) { return this .InsertAt(index, item) .AsImmutable(); } /// /// Renvoie une copie de la liste en en retirant l'élément à la position spécifiée /// /// Position de l'élément à retirer /// La nouvelle liste public ImmutableList RemoveAt(int index) { return this .SkipAt(index) .AsImmutable(); } /// /// Renvoie une copie de la liste en remplaçant l'élément à la position spécifiée /// /// Position à laquelle remplacer un élément /// Elément qui remplace l'élément existant /// La nouvelle liste public ImmutableList Replace(int index, T item) { return this .ReplaceAt(index, item) .AsImmutable(); } #endregion #region Interface implementations /// /// Renvoie la position de la première occurence de l'élément spécifié /// /// Elément recherché /// La position de l'élément s'il est présent dans la liste, sinon -1 public int IndexOf(T item) { if (_items == null) return -1; return _items.IndexOf(item); } /// /// Indique si la liste contient l'élément spécifié /// /// Elément recherché /// true si l'élément est présent dans la liste, sinon false public bool Contains(T item) { if (_items == null) return false; return _items.Contains(item); } /// /// Copie les éléments de la liste vers le tableau spécifié /// /// Tableau vers lequel les éléments sont copiés /// Position dans le tableau à laquelle la copie commence public void CopyTo(T[] array, int arrayIndex) { if (_items == null) return; _items.CopyTo(array, arrayIndex); } /// /// Renvoie le nombre d'éléments de la liste /// public int Count { get { if (_items == null) return 0; return _items.Count; } } /// /// Renvoie un énumérateur pour parcourir les éléments de la liste /// /// Un énumérateur pour parcourir les éléments de la liste public IEnumerator GetEnumerator() { if (_items == null) return Enumerable.Empty().GetEnumerator(); return _items.GetEnumerator(); } /// /// Détermine si cette liste est égale à la liste spécifiée. Deux listes immuables sont /// considérées égales si et seulement si elles comportent les mêmes éléments dans le /// même ordre. /// /// La liste avec laquelle on compare la liste courante /// true si les deux listes sont égales, false sinon public bool Equals(ImmutableList other) { if (other == null || this._hashCode != other._hashCode) return false; return this.SequenceEqual(other); } #endregion #region Explicit interface implementations /// /// Opération non valide. /// /// Position à laquelle insérer un élément /// Elément à insérer void IList.Insert(int index, T item) { throw new InvalidOperationException(); } /// /// Opération non valide. /// /// Position à laquelle supprimer un élément void IList.RemoveAt(int index) { throw new InvalidOperationException(); } /// /// Renvoie l'élément à la position spécifiée. L'accesseur set est une opération non valide. /// /// Position de l'élément à accéder /// La valeur de l'élément à l'index spécifié T IList.this[int index] { get { if (_items == null) throw new IndexOutOfRangeException(); return _items[index]; } set { throw new InvalidOperationException(); } } /// /// Opération non valide. /// /// Elément à ajouter void ICollection.Add(T item) { throw new InvalidOperationException(); } /// /// Opération non valide. /// void ICollection.Clear() { throw new InvalidOperationException(); } /// /// Indique si la collection est en lecture seule. Renvoie toujours true. /// bool ICollection.IsReadOnly { get { return true; } } /// /// Opération non valide. /// /// Elément à retirer /// true si l'élément a été retiré, sinon false. bool ICollection.Remove(T item) { throw new InvalidOperationException(); } /// /// Renvoie un énumérateur pour parcourir les éléments de la liste /// /// Un énumérateur pour parcourir les éléments de la liste IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } #endregion #region Overrides /// /// Détermine si cette instance est égale à l'objet spécifié. Deux listes immuables sont /// considérées égales si et seulement si elles comportent les mêmes éléments, dans le /// même ordre. /// /// L'objet avec lequel on compare cette instance /// true si obj est égal à cette instance, sinon false public override bool Equals(object obj) { if (obj is ImmutableList) { var other = (ImmutableList)obj; return this.Equals(other); } return false; } /// /// Renvoie le code de hachage de cette liste. /// /// Le code de hachage de cette liste public override int GetHashCode() { return _hashCode; } #endregion #region Private methods private int ComputeHash() { return this.MakeHashCode(_items); } #endregion } /// /// Fournit des méthodes statiques pour créer des listes immuables /// public static class ImmutableList { /// /// Crée une liste immuable à partir de la séquence spécifiée /// /// Type des éléments /// Séquence à partir de laquelle la liste immuable est crée /// Une liste immuable contenant les éléments spécifiés public static ImmutableList CreateFrom(IEnumerable source) { return new ImmutableList(source); } /// /// Crée une liste immuable à partir des éléments spécifiés /// /// Type des éléments /// Eléments à partir desquels la liste immuable est crée /// Une liste immuable contenant les éléments spécifiés public static ImmutableList Create(params T[] items) { return new ImmutableList(items); } /// /// Renvoie une liste immuable contenant les éléments d'une séquence /// /// Type des éléments /// Séquence à partir de laquelle la liste immuable est crée /// Une liste immuable contenant les éléments de la séquence public static ImmutableList AsImmutable(this IEnumerable source) { return new ImmutableList(source); } } }