Add ext. methods for calling List<T> methods on collection interfaces

This commit is contained in:
YoshiRulz 2021-03-26 18:41:24 +10:00
parent 3ff0eb33db
commit 3860199c52
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
2 changed files with 126 additions and 0 deletions

View File

@ -105,6 +105,21 @@ namespace BizHawk.Common.CollectionExtensions
throw new InvalidOperationException("Item not found");
}
/// <inheritdoc cref="List{T}.AddRange"/>
/// <remarks>
/// (This is an extension method which reimplements <see cref="List{T}.AddRange"/> for other <see cref="ICollection{T}">collections</see>.
/// It defers to the existing <see cref="List{T}.AddRange">AddRange</see> if the receiver's type is <see cref="List{T}"/> or a subclass.)
/// </remarks>
public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> collection)
{
if (list is List<T> listImpl)
{
listImpl.AddRange(collection);
return;
}
foreach (var item in collection) list.Add(item);
}
public static T? FirstOrNull<T>(this IEnumerable<T> list, Func<T, bool> predicate)
where T : struct
{
@ -113,5 +128,32 @@ namespace BizHawk.Common.CollectionExtensions
return t;
return null;
}
/// <inheritdoc cref="List{T}.RemoveAll"/>
/// <remarks>
/// (This is an extension method which reimplements <see cref="List{T}.RemoveAll"/> for other <see cref="ICollection{T}">collections</see>.
/// It defers to the existing <see cref="List{T}.RemoveAll">RemoveAll</see> if the receiver's type is <see cref="List{T}"/> or a subclass.)
/// </remarks>
public static int RemoveAll<T>(this ICollection<T> list, Predicate<T> match)
{
if (list is List<T> listImpl) return listImpl.RemoveAll(match);
var c = list.Count;
if (list is IList<T> iList)
{
for (var i = 0; i < iList.Count; i++)
{
if (match(iList[i])) iList.RemoveAt(i--);
}
}
else
{
foreach (var item in list.Where(item => match(item)) // can't simply cast to Func<T, bool>
.ToList()) // very important
{
list.Remove(item);
}
}
return c - list.Count;
}
}
}

View File

@ -0,0 +1,84 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BizHawk.Common.CollectionExtensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CE = BizHawk.Common.CollectionExtensions.CollectionExtensions;
namespace BizHawk.Tests.Common.CollectionExtensions
{
[TestClass]
public class CollectionExtensionTests
{
/// <summary>there isn't really an <see cref="IList{T}"/> implementor which isn't <see cref="List{T}"/> and isn't immutable... so I made one</summary>
private readonly struct ListImpl : IList<int>
{
private readonly IList<int> _wrapped;
public int Count => _wrapped.Count;
public bool IsReadOnly => false;
public ListImpl(params int[] init) => _wrapped = new List<int>(init);
public readonly int this[int index]
{
get => _wrapped[index];
set => throw new NotImplementedException();
}
public readonly void Add(int item) => throw new NotImplementedException();
public readonly void Clear() => throw new NotImplementedException();
public readonly bool Contains(int item) => throw new NotImplementedException();
public readonly void CopyTo(int[] array, int arrayIndex) => throw new NotImplementedException();
public readonly IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public readonly int IndexOf(int item) => throw new NotImplementedException();
public readonly void Insert(int index, int item) => throw new NotImplementedException();
public readonly bool Remove(int item) => throw new NotImplementedException();
public readonly void RemoveAt(int index) => _wrapped.RemoveAt(index);
}
[TestMethod]
public void TestAddRange()
{
List<int> a = new(new[] { 1, 2 });
CE.AddRange(a, new[] { 3, 4 });
Assert.AreEqual(4, a.Count, nameof(CE.AddRange) + " failed on List<int>");
SortedSet<int> b = new(new[] { 1, 2 });
b.AddRange(new[] { 3, 4 });
Assert.AreEqual(4, b.Count, nameof(CE.AddRange) + " failed on (ICollection<int> not List<int>)");
}
[TestMethod]
public void TestRemoveAll()
{
static bool Predicate(int i) => 2 <= i && i <= 3;
List<int> a = new(new[] { 1, 2, 3, 4 });
CE.RemoveAll(a, Predicate);
Assert.AreEqual(2, a.Count, nameof(CE.RemoveAll) + " failed on List<int>");
ListImpl b = new(1, 2, 3, 4);
b.RemoveAll(Predicate);
Assert.AreEqual(2, b.Count, nameof(CE.RemoveAll) + " failed on (IList<int> not List<int>)");
SortedSet<int> c = new(new[] { 1, 2, 3, 4 });
c.RemoveAll(Predicate);
Assert.AreEqual(2, c.Count, nameof(CE.RemoveAll) + " failed on (ICollection<int> not IList<int>)");
}
}
}