Cogs
General utilities to help with stuff in .NET Development, from Epiforge.
Supports netstandard2.1.
Libraries
Active Expressions
This library accepts a LambdaExpression and arguments to pass to it, dissects the LambdaExpression's body, and hooks into change notification events for properties (INotifyPropertyChanged), collections (INotifyCollectionChanged), and dictionaries (Cogs.Collections.INotifyDictionaryChanged).
// Employee implements INotifyPropertyChanged
var elizabeth = Employee.GetByName("Elizabeth");
var expr = ActiveExpression.Create(e => e.Name.Length, elizabeth);
// expr subscribed to elizabeth's PropertyChangedThen, as changes involving any elements of the expression occur, a chain of automatic re-evaluation will get kicked off, possibly causing the active expression's Value property to change.
var elizabeth = Employee.GetByName("Elizabeth");
var expr = ActiveExpression.Create(e => e.Name.Length, elizabeth);
// expr.Value == 9
elizabeth.Name = "Lizzy";
// expr.Value == 5Also, since exceptions may be encountered after an active expression was created due to subsequent element changes, active expressions also have a Fault property, which will be set to the exception that was encountered during evaluation.
var elizabeth = Employee.GetByName("Elizabeth");
var expr = ActiveExpression.Create(e => e.Name.Length, elizabeth);
// expr.Fault is null
elizabeth.Name = null;
// expr.Fault is NullReferenceExceptionActive expressions raise property change events of their own, so listen for those (kinda the whole point)!
var elizabeth = Employee.GetByName("Elizabeth");
var expr = ActiveExpression.Create(e => e.Name.Length, elizabeth);
expr.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "Fault")
{
// Whoops
}
else if (e.PropertyName == "Value")
{
// Do something
}
};When you dispose of your active expression, it will disconnect from all the events.
var elizabeth = Employee.GetByName("Elizabeth");
using (var expr = ActiveExpression.Create(e => e.Name.Length, elizabeth))
{
// expr subscribed to elizabeth's PropertyChanged
}
// expr unsubcribed from elizabeth's PropertyChangedActive expressions will also try to automatically dispose of disposable objects they create in the course of their evaluation when and where it makes sense.
Use the ActiveExpressionOptions class for more direct control over this behavior.
You can use the static property Optimizer to specify an optimization method to invoke automatically during the active expression creation process.
We recommend Tuomas Hietanen's Linq.Expression.Optimizer, the utilization of which would like like so:
ActiveExpression.Optimizer = ExpressionOptimizer.tryVisit;
var a = Expression.Parameter(typeof(bool));
var b = Expression.Parameter(typeof(bool));
var lambda = Expression.Lambda<Func<bool, bool, bool>>
(
Expression.AndAlso
(
Expression.Not(a),
Expression.Not(b)
),
a,
b
); // lambda explicitly defined as (a, b) => !a && !b
var expr = ActiveExpression.Create<bool>(lambda, false, false);
// optimizer has intervened and defined expr as (a, b) => !(a || b)
// (because Augustus De Morgan said they're essentially the same thing, but this involves less steps)Active Query
This library provides re-implementations of extension methods you know and love from System.Linq.Enumerable, but instead of returning Enumerable<T>s and simple values, these return ActiveEnumerable<T>s, ActiveDictionary<TKey, TValue>s, and ActiveValue<T>s.
This is because, unlike traditional LINQ extension methods, these extension methods continuously update their results until those results are disposed.
But... what could cause those updates?
- the source is enumerable, implements
INotifyCollectionChanged, and raises aCollectionChangedevent - the source is a dictionary, implements
Cogs.Collections.INotifyDictionaryChanged<TKey, TValue>, and raises aDictionaryChangedevent - the elements in the enumerable (or the values in the dictionary) implement
INotifyPropertyChangedand raise aPropertyChangedevent - a reference enclosed by a selector or a predicate passed to the extension method implements
INotifyCollectionChanged,Cogs.Collections.INotifyDictionaryChanged<TKey, TValue>, orINotifyPropertyChangedand raises one of their events
That last one might be a little surprising, but this is because all selectors and predicates passed to Active Query extension methods become active expressions (see above). This means that you will not be able to pass one that the Active Expressions library doesn't support (e.g. a lambda expression that can't be converted to an expression tree or that contains nodes that Active Expressions doesn't deal with). But, in exchange for this, you get all kinds of notification plumbing that's just handled for you behind the scenes.
Suppose, for example, you're working on an app that displays a list of notes and you want the notes to be shown in descending order of when they were last edited.
var notes = new ObservableCollection<Note>();
var orderedNotes = notes.ActiveOrderBy(note => note.LastEdited, isDescending: true);
notesViewControl.ItemsSource = orderedNotes;From then on, as you add Notes to the notes observable collection, the ActiveEnumerable<Note> named orderedNotes will be kept ordered so that notesViewControl displays them in the preferred order.
Since the ActiveEnumerable<T> is automatically subscribing to events for you, you do need to call Dispose on it when you don't need it any more.
void Page_Unload(object sender, EventArgs e)
{
orderedNotes.Dispose();
}But, you may ask, what happens if things are a little bit more complicated because of background work? Suppose...
SynchronizedObservableCollection<Note> notes;
ActiveEnumerable<Note> orderedNotes;
Task.Run(() =>
{
notes = new SynchronizedObservableCollection<Note>();
orderedNotes = notes.ActiveOrderBy(note => note.LastEdited, isDescending: true);
});Since we called the Cogs.Collections.Synchronized.SynchronizedObservableCollection constructor in the context of a TPL Task and without specifying a SynchronizationContext, operations performed on it will not be in the context of our UI thread.
Manipulating this collection on a background thread might be desirable, but there will be a big problem if we bind a UI control to it, since non-UI threads shouldn't be messing with UI controls.
For this specific reason, Active Query offers a special extension method that will perform the final operations on an enumerable (or dictionary) using a specific SynchronizationContext.
var uiContext = SynchronizationContext.Current;
SynchronizedObservableCollection<Note> notes;
ActiveEnumerable<Note> orderedNotes;
ActiveEnumerable<Note> notesForBinding;
Task.Run(() =>
{
notes = new SynchronizedObservableCollection<Note>();
orderedNotes = notes.ActiveOrderBy(note => note.LastEdited, isDescending: true);
notesForBinding = orderedNotes.SwitchContext(uiContext);
});Or, if you call SwitchContext without any arguments but when you know you're already running in the UI's context, it will assume you want to switch to that.
SynchronizedObservableCollection<Note> notes;
ActiveEnumerable<Note> orderedNotes;
await Task.Run(() =>
{
notes = new SynchronizedObservableCollection<Note>();
orderedNotes = notes.ActiveOrderBy(note => note.LastEdited, isDescending: true);
});
var notesForBinding = orderedNotes.SwitchContext();But, keep in mind that no Active Query extension methods mutate the objects for which they are called, which means now you have two things to dispose, and in the right order!
void Page_Unload(object sender, EventArgs e)
{
notesForBinding.Dispose();
orderedNotes.Dispose();
}Ahh, but what about exceptions?
Well, active expressions expose a Fault property and raise PropertyChanging and PropertyChanged events for it, but... you don't really see those active expressions as an Active Query caller, do ya?
For that reason, Active Query introduces the INotifyElementFaultChanges interface, which is implemented by ActiveEnumerable<T>, ActiveDictionary<TKey, TValue>, and ActiveValue<T>.
You may subscribe to its ElementFaultChanging and ElementFaultChanged events to be notified when an active expression runs into a problem.
You may also call the GetElementFaults method at any time to retrieve a list of the elements (or key/value pairs) that have active expressions that are currently faulted and what the exception was in each case.
As with the Active Expressions library, you can use the static property Optimizer to specify an optimization method to invoke automatically during the active expression creation process.
However, please note that Active Query also has its own version of this property on the ActiveQueryOptions static class.
If you are not using Active Expressions directly, we recommend using Active Query's property instead because the optimizer will be called only once per extension method call in that case, no matter how many elements or key/value pairs are processed by it.
Optimize your optimization, yo.
Collections
This library provides a number of utilities surrounding collections:
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.INotifyGenericCollectionChanged<T>is similar to the BCL'sINotifyCollectionChangedexcept that it is a generic and therefore provides event arguments aware of the type of the collection.NullableKeyDictionary<TKey, TValue>andNullableKeySortedDictionary<TKey, TValue>are very slim implementations ofIDictionary<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)ObservableDictionary<TKey, TValue>andObservableSortedDictionary<TKey, TValue>are counterparts to the BCL'sDictionary<TKey, TValue>andSortedDictionary<TKey, TValue>, respectively, that implement the also includedIRangeDictionary<TKey, TValue>andINotifyDictionaryChanged<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.
Components
For now, all this library offers is the PropertyChangeNotifier class, which you may inherit from to quickly get all the property utilities we're all tired of copying and pasting everywhere.
Just call the protected OnPropertyChanged and OnPropertyChanging methods at the appropriate times from setters and compiler services will figure out what property you're in.
Or, if all you need to do is set the value of a field, SetBackedProperty couldn't make it any easier or convenient to handle that as efficiently as possible.
Disposal
Much like the Components library, this library features base classes that handle things we've written a thousand times over, this time involving disposal.
If you want to go with an implementation of the tried and true IDisposable, just inherit from SyncDisposable. Want a taste of the new IAsyncDisposable?
Then, inherit from AsyncDisposable.
Or, if you want to support both, there's Disposable.
Each of these features abstract methods to actually do your disposal.
But all of the base classes feature:
- proper implementation of the finalizer and use of
GC.SuppressFinalize - monitored access to disposal to ensure it can't happen twice
- the ability to override or "cancel" disposal by returning false from the abstract methods (e.g. you're reference counting and only want to dispose when your counter reaches zero)
- a protected
ThrowIfDisposedmethod you can call to before doing anything that requires you haven't been disposed - an
IsDisposedproperty the value (and change notifications) of which are handled for you
This library provides the IDisposalStatus interface, which defines the IsDisposed property and all the base classes implement it.
Lastly, it provides the INotifyDisposing, INotifyDisposed, and INotifyDisposalOverridden interfaces, which add events that notify of these occurrences.
If you're using the base classes in this library, you don't need to worry about unregistering handlers.
The base classes drop all the references in the events' invocation lists on their own.
We're not trying to create leaks here!
Reflection
This library has useful tools for when you can't be certain of some things at compile time, such as types, methods, etc. While .NET reflection is immensely powerful, it's not very quick. To address this, this library offers the following classes:
FastComparer- provides a method for comparing instances of a type that is not known at compile timeFastConstructorInfo- provides a method for invoking a constructor that is not known at compile timeFastDefault- provides a method for getting the default value of a type that is not known at compile timeFastEqualityComparer- provides methods for testing equality of and getting hash codes for instances of a type that is not known at compile timeFastMethodInfo- provides a method for invoking a method that is not known at compile time
All of the above classes use reflection to initialize utilities for types at runtime, however they create delegates to perform at much better speeds and cache instances of themselves to avoid having to perform the same reflection twice. And yes, the caching is thread-safe.
Synchronized Collections
Good idea: binding UI elements to observable collections. Bad idea: manipulating observable collections bound to UI elements from background threads. Why? Because the collection change notification event handlers will be executed on non-UI threads, which cannot safely manipulate the UI. So, I guess we need to carefully marshal calls over to the UI thread whenever we manipulate or even read those observable collections, right?
Not anymore.
Introducing the SynchronizedObservableCollection<T>, SynchronizedObservableDictionary<TKey, TValue>, and SynchronizedObservableSortedDictionary<TKey, TValue> classes.
Create them on UI threads.
Or, pass the UI thread's synchronization context to their constructors.
Then, any time they are touched, the call is marshalled to the context of the appropriate thread.
They even include async alternatives to every method and indexer just in case you would like to be well-behaved and not block worker threads just because the UI thread is busy.
I mean, no judgment. We just don't like sending threads to thread jail.
Last, but not least, each of them also has an array of range methods to handle performing multiple operations at once when you know you'll need to in advanced and would like to avoid O(2n) context switching.
Threading
This is where we keep all our utilities for multi-threaded stuff.
AsyncExtensions- provides extensions for dealing with async utilities likeTaskCompletionSource<TResult>AsyncSynchronizationContext- provides a synchronization context for the Task Parallel LibraryISynchronized- represents an object the operations of which occur on a specific synchronization context (used extensively by the Synchronized Collections library, above)SynchronizedExtensions- provides extensions for executing operations with instances ofSystem.Threading.SynchronizationContextandISynchronized
License
Contributing
Click here to learn how to contribute.
Acknowledgements
Makes use of the glorious AsyncEx library by Stephen Cleary.
