介绍
我已经实现了两种算法,一种用于排序,一种用于顺序收集。两者都支持空值和重复值,并且以相同的方式工作:
它们产生CollectionModification<LeftItemType,RightItemType>
类似于CollectionChangedEventArgs<T>
( reference ) 的 return ,后者可用于同步集合。
关于收益率:
当使用一种或另一种算法将左侧项目(参考集合)与右侧项目进行比较时,您可以CollectionModification
在它们返回后立即应用返回的每个收益,但这可能导致“集合已修改”-异常(例如在使用List<T>.GetEnumerator
) 时。为了防止这种情况,两种算法都可以使用可索引集合作为即将发生变异的参考集合。您只需使用YieldIteratorInfluencedReadOnlyListExtensionsYieldIteratorInfluencedReadOnlyList<ItemType>
中的扩展方法将引用集合与 (abstract) 一起包装。:)
SortedCollectionModifications
第一个算法适用于升序或降序列表并使用IComparer<T>
.
/// <summary>
/// The algorithm creates modifications that can transform one collection into another collection.
/// The collection modifications may be used to transform <paramref name="leftItems"/>.
/// Assumes <paramref name="leftItems"/> and <paramref name="rightItems"/> to be sorted by that order you specify by <paramref name="collectionOrder"/>.
/// Duplications are allowed but take into account that duplications are yielded as they are appearing.
/// </summary>
/// <typeparam name="LeftItemType">The type of left items.</typeparam>
/// <typeparam name="RightItemType">The type of right items.</typeparam>
/// <typeparam name="ComparablePartType">The type of the comparable part of left item and right item.</typeparam>
/// <param name="leftItems">The collection you want to have transformed.</param>
/// <param name="getComparablePartOfLeftItem">The part of left item that is comparable with part of right item.</param>
/// <param name="rightItems">The collection in which <paramref name="leftItems"/> could be transformed.</param>
/// <param name="getComparablePartOfRightItem">The part of right item that is comparable with part of left item.</param>
/// <param name="collectionOrder">the presumed order of items to be used to determine <see cref="IComparer{T}.Compare(T, T)"/> argument assignment.</param>
/// <param name="comparer">The comparer to be used to compare comparable parts of left and right item.</param>
/// <param name="yieldCapabilities">The yieldCapabilities that regulates how <paramref name="leftItems"/> and <paramref name="rightItems"/> are synchronized.</param>
/// <returns>The collection modifications.</returns>
/// <exception cref="ArgumentNullException">Thrown when non-nullable arguments are null.</exception>
public static IEnumerable<CollectionModification<LeftItemType, RightItemType>> YieldCollectionModifications<LeftItemType, RightItemType, ComparablePartType>(
IEnumerable<LeftItemType> leftItems,
Func<LeftItemType, ComparablePartType> getComparablePartOfLeftItem,
IEnumerable<RightItemType> rightItems,
Func<RightItemType, ComparablePartType> getComparablePartOfRightItem,
SortedCollectionOrder collectionOrder,
IComparer<ComparablePartType> comparer,
CollectionModificationsYieldCapabilities yieldCapabilities)
Python 算法灵感来自:有序列表的两个实例的高效同步。
EqualityTrailingCollectionModifications
第二种算法适用于任何顺序并使用IEqualityComparer<T>
.
/// <summary>
/// The algorithm creates modifications that can transform one collection into another collection.
/// The collection modifications may be used to transform <paramref name="leftItems"/>.
/// The more the collection is synchronized in an orderly way, the more efficient the algorithm is.
/// Duplications are allowed but take into account that duplications are yielded as they are appearing.
/// </summary>
/// <typeparam name="LeftItemType">The type of left items.</typeparam>
/// <typeparam name="RightItemType">The type of right items.</typeparam>
/// <typeparam name="ComparablePartType">The type of the comparable part of left item and right item.</typeparam>
/// <param name="leftItems">The collection you want to have transformed.</param>
/// <param name="getComparablePartOfLeftItem">The part of left item that is comparable with part of right item.</param>
/// <param name="rightItems">The collection in which <paramref name="leftItems"/> could be transformed.</param>
/// <param name="getComparablePartOfRightItem">The part of right item that is comparable with part of left item.</param>
/// <param name="equalityComparer">The equality comparer to be used to compare comparable parts.</param>
/// <param name="yieldCapabilities">The yield capabilities, e.g. only insert or only remove.</param>
/// <returns>The collection modifications.</returns>
/// <exception cref="ArgumentNullException">Thrown when non-nullable arguments are null.</exception>
public static IEnumerable<CollectionModification<LeftItemType, RightItemType>> YieldCollectionModifications<LeftItemType, RightItemType, ComparablePartType>(
IEnumerable<LeftItemType> leftItems,
Func<LeftItemType, ComparablePartType> getComparablePartOfLeftItem,
IEnumerable<RightItemType> rightItems,
Func<RightItemType, ComparablePartType> getComparablePartOfRightItem,
IEqualityComparer<ComparablePartType>? equalityComparer,
CollectionModificationsYieldCapabilities yieldCapabilities)
where ComparablePartType : notnull
要求
需要以下框架之一
- .NET 标准 2.0
- .NET 核心 3.1
- .NET 5.0
这两种算法都是使用自定义实现类型(IndexDirectory
,NullableKeyDictionary
等LinkedBucketList
)创建的,所以我不能在这里简单地复制粘贴代码,所以我想参考我的以下包:
执行
预期课程
帐户:
public class Account
{
public Account(int id) =>
Id = id;
public int Id { get; }
public double Amount { get; }
}
以及以下集合项相等比较器类:
AccountEqualityComparer:
public class AccountEqualityComparer : EqualityComparer<Account>
{
public new static AccountEqualityComparer Default = new AccountEqualityComparer();
public override bool Equals([AllowNull] Account x, [AllowNull] Account y) =>
ReferenceEquals(x, y) || (!(x is null && y is null) && x.Id.Equals(y.Id));
public override int GetHashCode([DisallowNull] Account obj) =>
obj.Id;
}
“我的课程
AccountCollectionViewModel:
using Teronis.Collections.Algorithms.Modifications;
using Teronis.Collections.Synchronization;
using Teronis.Collections.Synchronization.Extensions;
using Teronis.Reflection;
public class AccountCollectionViewModel : SyncingCollectionViewModel<Account, Account>
{
public AccountCollectionViewModel()
: base(CollectionSynchronizationMethod.Sequential(AccountEqualityComparer.Default))
{
// In case of SyncingCollectionViewModel, we have to pass a synchronization method.
//
// Sequential means any order
//
}
protected override Account CreateSubItem(Account superItem) =>
superItem;
protected override void ApplyCollectionItemReplace(in ApplyingCollectionModificationBundle modificationBundle)
{
foreach (var (oldItem, newItem) in modificationBundle.OldSuperItemsNewSuperItemsModification.YieldTuplesForOldItemNewItemReplace())
{
// Implementation detail: update left public property values by right public property values.
TeronisReflectionUtils.UpdateEntityVariables(oldItem, newItem);
}
}
}
程序:
using System.Diagnostics;
using System.Linq;
class Program
{
static void Main()
{
// Arrange
var collection = new AccountCollectionViewModel();
var initialData = new Account[] {
new Account(5) { Amount = 0 },
new Account(7) { Amount = 0 },
new Account(3) { Amount = 0 }
};
var newData = new Account[] {
new Account(5) { Amount = 10 },
/* Account by ID 7 got removed .. */
/* but account by ID 8 is new. */
new Account(8) { Amount = 10 },
new Account(3) { Amount = 10 }
};
// Act
collection.SynchronizeCollection(initialData);
// Assert
Debug.Assert(collection.SubItems.ElementAt(1).Id == 7, "The account at index 1 has not the ID 7.");
Debug.Assert(collection.SubItems.All(x => x.Amount == 0), "Not all accounts have an amount of 0.");
// Act
collection.SynchronizeCollection(newData);
// Assert
Debug.Assert(collection.SubItems.ElementAt(1).Id == 8, "The account at index 1 has not the ID 8.");
Debug.Assert(collection.SubItems.All(x => x.Amount == 10), "Not all accounts have an amount of 10.");
;
}
}
你可以看到我使用了SyncingCollectionViewModel,一个非常“重”的类型。那是因为我还没有完成轻量级的SynchronizableCollection实现(添加、删除、替换等虚拟方法缺失)。