using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; #pragma warning disable CS8603 // Possible null reference return. #pragma warning disable CS8601 // Possible null reference assignment. namespace EasyDevCore.Common.Wrapper { /// /// Defines common methods for INameValueList and IReadOnlyNameValueList. /// public interface INameValueListBase { /// /// Returns the first Value of the given Name if one exists, otherwise null or default value. /// TValue FirstOrDefault(string name); /// /// Gets the first Value of the given Name, if one exists. /// /// true if any item of the given name is found, otherwise false. bool TryGetFirst(string name, out TValue value); /// /// Gets all Values of the given Name. /// IEnumerable GetAll(string name); /// /// True if any items with the given Name exist. /// bool Contains(string name); /// /// True if any item with the given Name and Value exists. /// bool Contains(string name, TValue value); } /// /// Defines an ordered collection of Name/Value pairs where duplicate names are allowed but aren't typical. /// public interface INameValueList : IList<(string Name, TValue Value)>, INameValueListBase { /// /// Adds a new Name/Value pair. /// void Add(string name, TValue value); /// /// Replaces the first occurrence of the given Name with the given Value and removes any others, /// or adds a new Name/Value pair if none exist. /// void AddOrReplace(string name, TValue value); /// /// Removes all items of the given Name. /// /// true if any item of the given name is found, otherwise false. bool Remove(string name); } /// /// Defines a read-only ordered collection of Name/Value pairs where duplicate names are allowed but aren't typical. /// public interface IReadOnlyNameValueList : IReadOnlyList<(string Name, TValue Value)>, INameValueListBase { } /// /// An ordered collection of Name/Value pairs where duplicate names are allowed but aren't typical. /// Useful for things where a dictionary would work great if not for those pesky edge cases (headers, cookies, etc). /// public class NameValueList : List<(string Name, TValue Value)>, INameValueList, IReadOnlyNameValueList { private StringComparison _ComparisonType; /// /// Instantiates a new empty NameValueList. /// public NameValueList(StringComparison comparisonType) { _ComparisonType = comparisonType; } /// /// Instantiates a new NameValueList with the Name/Value pairs provided. /// public NameValueList(IEnumerable<(string Name, TValue Value)> items, StringComparison comparisonType) { _ComparisonType = comparisonType; AddRange(items); } /// public void Add(string name, TValue value) => Add((name, value)); /// public void AddOrReplace(string name, TValue value) { var i = 0; var replaced = false; while (i < this.Count) { if (!string.Equals(this[i].Name, name, _ComparisonType)) i++; else if (replaced) this.RemoveAt(i); else { this[i] = (name, value); replaced = true; i++; } } if (!replaced) this.Add(name, value); } /// public bool Remove(string name) => RemoveAll(x => string.Equals(x.Name, name, _ComparisonType)) > 0; /// public TValue FirstOrDefault(string name) => GetAll(name).FirstOrDefault(); /// public bool TryGetFirst(string name, out TValue value) { foreach (var v in GetAll(name)) { value = v; return true; } value = default; return false; } /// public IEnumerable GetAll(string name) => this .Where(x => string.Equals(x.Name, name, _ComparisonType)) .Select(x => x.Value); /// public bool Contains(string name) => this.Any(x => string.Equals(x.Name, name, _ComparisonType)); /// public bool Contains(string name, TValue value) => Contains((name, value)); } }