Epiforge.Extensions.Collections 4.0.0

dotnet add package Epiforge.Extensions.Collections --version 4.0.0
                    
NuGet\Install-Package Epiforge.Extensions.Collections -Version 4.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Epiforge.Extensions.Collections" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Epiforge.Extensions.Collections" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Epiforge.Extensions.Collections" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Epiforge.Extensions.Collections --version 4.0.0
                    
#r "nuget: Epiforge.Extensions.Collections, 4.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Epiforge.Extensions.Collections@4.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Epiforge.Extensions.Collections&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Epiforge.Extensions.Collections&version=4.0.0
                    
Install as a Cake Tool

This library provides a number of extension methods for collections and dictionaries:

  • EnumerableExtensions, providing:
    • FindIndex - Finds the index of the first element in the source that satisfies the specified predicate
    • FindLastIndex - Finds the index of the last element in the source that satisfies the specified predicate
    • FindIndicies - Finds the indicies of the elements in the source that satisfy the specified predicate
    • IndexOf - Finds the first index of the specified item in the source
    • LastIndexOf - Finds the last index of the specified item in the source
    • IndiciesOf - Finds the indicies of the specified item in the source
  • DictionaryExtensions, providing:
    • GetOrAdd - Adds a key/value pair to the specified IDictionary or IDictionary<TKey, TValue> by using the specified function if the key does not already exist (returns the new value, or the existing value if the key exists)
    • TryRemove - Attempts to remove and return the value that has the specified key from the specified IDictionary or IDictionary<TKey, TValue>

Generic

  • ReadOnlyDictionary<TKey, TValue> is a read-only wrapper for any classes implementing IReadOnlyDictionary<TKey, TValue>
  • ReadOnlyRangeDictionary<TKey, TValue> is a read-only wrapper for any classes implementing IReadOnlyRangeDictionary<TKey, TValue>
  • ReadOnlyConcurrentDictionary<TKey, TValue> is a read-only wrapper for ObservableConcurrentDictionary<TKey, TValue>
  • ReversedComparer<T> is a comparer that reverses the comparison of another comparer (this is useful when you want to sort a list in the opposite order of the default sort order)
  • IHashKeys<TKey> is implemented by keyed data structures that use an IEqualityComparer<TKey> to decide key equality, so that a consumer can discover the comparer a dictionary is actually using rather than assume the default (the observable queries in Epiforge.Extensions.Expressions do exactly this)
  • PrefixWeightedSequence<T> is a sequence in which every position carries a weight. Insertion, removal, movement, and changing a weight are all logarithmic in the number of positions, as is finding a position by index, by the sum of the weights before it, or by which position a given offset falls within. PrefixWeightedSequenceNode<T> is the handle to a position and remains valid for as long as its item remains in the sequence, so you can hold onto one instead of re-finding an index after every change. When you are looking for a position near one you already have in hand, hand that node back with NodeAtFrom or NodeAtWeightFrom and the search costs the logarithm of the distance between the two rather than the logarithm of the whole sequence, which makes walking a sequence by position nearly as cheap as enumerating it.

ObjectModel

  • ObservableDictionary<TKey, TValue>, ObservableSortedDictionary<TKey, TValue>, ObservableConcurrentDictionary<TKey, TValue> are counterparts to the BCL's Dictionary<TKey, TValue>, SortedDictionary<TKey, TValue>, and ConcurrentDictionary<TKey, TValue>, respectively, that implement the also included IRangeDictionary<TKey, TValue> and INotifyDictionaryChanged<TKey, TValue>. Ever want to add multiple items to a dictionary at once... or keep an eye on what's being done to it? Now you can.
  • ObservableRangeCollection<T> is a counterpart to the BCL's ObservableCollection<T> which implements:
    • AddRange - Adds objects to the end of the collection
    • GetAndRemoveAll - Removes all object from the collection that satisfy a predicate
    • GetAndRemoveAt - Gets the element at the specified index and removes it from the collection
    • GetRange - Gets the elements in the range starting at the specified index and of the specified length
    • InsertRange - Inserts elements into the collection at the specified index
    • MoveRange - Moves the items at the specified index to a new location in the collection
    • RemoveAll - Removes all object from the collection that satisfy a predicate
    • RemoveRange - Removes the specified items from the collection or removes the specified range of items from the collection
    • ReplaceAll - Replace all items in the collection with the items in the specified collection
    • ReplaceRange - Replaces the specified range of items from the collection with the items in the specified collection
    • Reset - Resets the collection with the specified collection of items
    • ResetRemovingAll - Removes all objects from the collection that satisfy a predicate, announcing it with a single event
  • ReadOnlyObservableRangeDictionary<TKey, TValue> is a read-only wrapper for any classes implementing IReadOnlyObservableRangeDictionary<TKey, TValue>. It subscribes to what it wraps, so dispose of it when you are done with it.
  • ReadOnlyObservableRangeCollection<T> is a read-only wrapper for any classes implementing IReadOnlyObservableRangeCollection<T>. It subscribes to what it wraps, so dispose of it when you are done with it.

Specialized

  • EquatableList<T> is an immutable list of items which may be compared with other instances of the same type and produces a hash code based on the permutation of its contents.
  • NullableKeyDictionary<TKey, TValue> and NullableKeySortedDictionary<TKey, TValue> are very slim implementations of IDictionary<TKey, TValue> that allow a single null key (useful for some edge cases in which a null key is simply going to happen and you need to be able to deal with it; otherwise, use other dictionary classes).
  • OrderedHashSet<T> is a counterpart to the BCL's HashSet<T> that maintains the order of the elements in the set. All operations are still O(1), just like the original, but if you enumerate over it you will get elements in the exact order they were added. There are also methods for manipulating the order.
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Epiforge.Extensions.Collections:

Package Downloads
Epiforge.Extensions.Expressions

This package assists in operating with expressions.

Epiforge.Extensions.Frameworks.WPF

This package assists in developing WPF-based applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.0 0 8/31/2026
3.1.0 96 8/29/2026
3.0.0 176 8/27/2026
2.1.1 435 4/14/2026
2.0.1 341 12/24/2025
2.0.0 436 4/17/2025
1.5.0 404 8/24/2023
1.4.0 350 8/24/2023
1.3.2 682 8/15/2023
1.3.1 500 8/13/2023
1.2.3 307 8/12/2023
1.2.1 309 8/10/2023
1.1.0 321 8/8/2023
1.0.0 311 6/23/2023

An ObservableRangeCollection which raises a single event for a range operation now also announces that its indexer has changed, as it already did when raising an event for each element and as the collection it derives from has always done. Only the count was announced before, so anything bound to an indexed position refreshed under one setting of RaiseCollectionChangedEventsForIndividualElements and not the other, decided by a constructor argument. MoveRange, which announced nothing at all in that mode, now announces the indexer; a replacement of the same length announces the indexer without announcing the count, which has not changed.
NotifyDictionaryChangedEventArgs now throws ArgumentNullException when the items it is handed are null. That parameter was already declared as never null, and a null produced an event carrying nothing rather than an error, which is a wrong notification instead of a lenient one. The constructor which takes new and old items separately still accepts null for either side, where null means that side has no items.
RemoveAll, GetAndRemoveAll and the two RemoveRange overloads which take items on an ObservableRangeCollection now honour RaiseCollectionChangedEventsForIndividualElements, which they had ignored. That flag exists so a collection can be bound to a consumer which cannot process an event carrying more than one item, and none of these methods ever emitted one, so no collection which set the flag is affected. A collection which did not set it now receives one event for each run of adjacent items removed rather than one for each item, so removing a contiguous block raises a single event; removing alternating items raises the same number of events as before. RemoveRange also visits the collection once instead of searching it for each item supplied, which removes a cost that grew with the size of the collection multiplied by the number of items to remove; it now removes items in the order they occur in the collection rather than the order they were supplied, which changes the order events are raised in but not which items are removed.
Every range operation on an ObservableRangeCollection which raises a single event now announces its changed properties before that event rather than after, matching the collection it derives from and the order it already used when raising an event for each element.
An ObservableDictionary, ObservableSortedDictionary or ObservableConcurrentDictionary now announces that its indexer has changed whenever its contents change, as the observable collection in this package already did and as anything bound to a particular key needs in order to refresh. Only the count was announced before, so replacing the value stored under a key announced nothing at all.
Adding or removing a single element from any of those three dictionaries builds the event describing it once rather than twice. The single key and value were placed in a collection, and that collection was then copied into another, on every such change whether or not anything was listening.
ReplaceAll and ReplaceRange on an ObservableRangeCollection no longer describe a replacement of one number of items by a different number as a single Replace event. A consumer reading such an event pairwise, taking the first new item to stand for the first old one, ran out of items on one side with no way to know which. Such a replacement now replaces as many items as both sides have and then adds or removes the surplus, so replacing four items with three is a Replace of three followed by a Remove of one, and replacing two with four is a Replace of two followed by an Add of two. A replacement of equal length is still a single Replace, replacing everything with nothing remains a single Remove, replacing nothing with something is now a single Add, and replacing nothing with nothing announces nothing at all rather than an empty event.
ObservableRangeCollection gains ResetRemovingAll, which removes every item satisfying a predicate and announces it with one Reset event. The cost of RemoveAll grows with the size of the collection multiplied by the number of items removed, because it shifts the remainder down for each one; the cost of the new method is a single pass, which for a sixteen thousand element collection with three quarters of it matching is 56 microseconds and 62.6 kilobytes. RemoveAll remains the one to use when consumers need to be told which items left and from where, and it allocates less when the predicate matches under about four percent of the collection.
None of the three dictionaries builds the event describing a change unless something is listening for it. The event was constructed at each call site before the method which raises it could check, so a dictionary nothing was subscribed to still paid to describe changes no one received; a removal from one now allocates nothing whatsoever. A derivation which overrides OnChanged in order to observe changes rather than to raise them will no longer be called when nothing is subscribed, and should subscribe to DictionaryChanged instead.
An ObservableRangeCollection whose elements are themselves lists now describes the removal of one of them as a single item rather than as that item's contents. The event arguments have separate constructors for one changed item and for a list of them, and an element which implements IList selected the second, so removing one two-element array from such a collection announced that two elements had been removed.