IX.StandardExtensions documentation

Abstractions

There are a lot of abstractions available in IX.StandarExtensions that seek to eliminate the hard dependencies to classes. The most widely-used are abstractions for the System.IO classes.

System.IO abstractions

Using these classes and interfaces, you can abstract away File, Directory and Path. These abstractions are located in the IX.System.IO namespace.

You can inject an IFile, for example, which abstracts away all functionality in the File class:

private readonly IFile _fileShim;

In your code, you can use it as you would normally use the File class, but, instead of calling static methods, you call instance methods on your _fileShim instance:

using (var fileStream = _fileShim.OpenRead(fileToOpenPath))
{
    // Do something with the fileStream variable
}

Then, when testing, all methods can be mocked, as in the example below (using Moq):

Mock<IFile> fileShimMock = new(MockBehavior.Strict);

fileShimMock.Setup(p => p.OpenRead("somePath.txt"))
            .Returns(myControlledStream);

// Do the test here

fileShimMock.Verify(p => p.OpenRead("somePath.txt"), Times.Once());

All methods of the static classes are abstracted, and are also abstracted to the highest level of the framework API that is actually usable in that framework. Apart from allowing all methods to be mocked, this abstraction allows any code written in any supported framework version to be written into easily-upgradeable forms.

System.Threading abstractions

Using these classes and interfaces, you can abstract away the AutoResetEvent, ManualResetEvent, ManualResetEventSlim and ReaderWriterLockSlim classes. These abstractions are located in the IX.System.Threading namespace.

Furthermore, an extra layer of abstraction is added with the ISetResetEvent and IReaderWriterLock interfaces. These give complete control and visibility over thread synchronization mechanisms, allowing developers to do in-depth tests of their code and verify both positive and negative aspects at runtime.

To abstract an AutoResetEvent, simply use the AutoResetEvent class from the IX.System.Threading namespace:

private IX.System.Threading.AutoResetEvent _resetEvent;

protected ISetResetEvent SynchronizationEvent => _resetEvent;

In code that needs to wait, you can use:

await SynchronizationEvent;

Or, if you prefer the older style, need to synchronously wait, or cannot use the await keyword due to language constraints, or need to deal with timeouts, you can use:

if (!SynchronizationEvent.WaitOne(_timeout))
{
   // Do something here for timeout exceeded
}

// Do something here for no timeout exceeded

Then, in any code that needs to unlock threads, you can use:

SynchronizationEvent.Set();

This can then be easily mocked in a manner similar to this:

Moq<ISetResetEvent> eventMock = new(MockBehavior.Strict);

eventMock.Setup(p => p.WaitOne(someTimeout)).Returns(true);
eventMock.Setup(p => p.Set());

// Do some tests

eventMock.Verify(p => p.WaitOne(someTimeout), Times.Once());
eventMock.Verify(p => p.Set(), Times.Once());

Similarly, to abstract away ReaderWriterLockSlim, you need only use the ReaderWriterLockSlim class from the IX.System.Threading namespace:

IX.System.Threading.ReaderWriterLockSlim rwl = new();

You can then use its locking abilities like:

if (rwl.TryEnterReadLock())
{
    // Do something when the lock couldn't be acquired
}

// Do something when the lock could be acquired

But the true strength of this abstraction lies in the ReaderWriterSynchronizedBase class described below.

Base classes

There are a few base classes available that remove the boilerplate that is required for a lot of advanced work. There are classes like:

  • DisposableBase - a base class that implements the IDisposable pattern in a developer-friendly way
  • SynchronizationContextInvokerBase - a base class that offers a way to invoke on synchronization contexts, depending on what was set at construction time or, maybe, a default - especially useful in UI
  • NotifyPropertyChangedBase - a base class for objects that need to implement property change notifications
  • NotifyCollectionChangedInvokerBase - a base class for collections that need to implement change notifications
  • ReaderWriterSynchronizedBase - a base class for objects that need thread synchronization and thread safety facilities
  • ViewModelBase - a base class for the view-model pattern

DisposableBase

The DisposableBase class implements IDisposable and IAsyncDisposable (where available), and allows a class that inherits from it to select how to best implement that pattern to their own needs.

For example, a chain for disposing within the managed context (IDisposable.Dispose is called, maybe as a result of a using block) could be used like this:

protected override void DisposeManagedContext()
{
    // Do something dispose-related here

    base.DisposeManagedContext();
}

Since the DisposableBase always keeps a (thread-safe) marker of whether or not the Dispose method had been called or not, it is possible to automatically throw ObjectDisposedException from your methods:

public void DoSomething()
{
    // Current object not disposed
    this.ThrowIfCurrentObjectDisposed();
     
    // ... do whatever you need to
}

Any class that inherits can also use invocations that run in case the object hasn't been already disposed:

public void DoSomething() =>
    this.InvokeIfNotDisposed(() => /* ... do something ... */);

SynchronizationContextInvokerBase

The SynchronizationContextInvokerBase class allows invocations on a set or on a default synchronization context, like in events:

private void SomeButton_Clicked(
    object? sender,
    EventArgs e) =>
    Invoke(() => { /* ... This runs on the synchronization context ... */ });

The Invoke method can be configured to either invoke synchronously (default) or asynchyronously, by setting the IX.StandardExtensions.ComponentModel.EnvironmentSettings.InvokeAsynchronously static property:

IX.StandardExtensions.ComponentModel.EnvironmentSettings.InvokeAsynchronously = true;

You can also explicitly request synchronous invocation:

private void SomeButton_Clicked(
    object? sender,
    EventArgs e) =>
    InvokeSend(() => { /* ... This runs synchronously on the synchronization context ... */ });

...or asynchronous invocation:

private void SomeButton_Clicked(
    object? sender,
    EventArgs e) =>
    InvokePost(() => { /* ... This runs asynchronously on the synchronization context ... */ });

You can either set a specific synchronization context through the protected constructor:

protected void MyClassConstructor()
    : base(new DispatcherSynchronizationContext(Application.Dispatcher))
{
    // ... do constructor stuff
}

...or you can set the default one in IX.StandardExtensions.ComponentModel.EnvironmentSettings:

IX.StandardExtensions.ComponentModel.EnvironmentSettings.BackupSynchronizationContext = new DispatcherSynchronizationContext(Application.Dispatcher);

You can always suppress the current synchronization context (and use the default one) by setting:

IX.StandardExtensions.ComponentModel.EnvironmentSettings.AlwaysSuppressCurrentSynchronizationContext = true;

You can get the applicable synchronization context by calling the GetUsableSynchronizationContext method, like such:

var currentSynchronizationContext = IX.StandardExtensions.ComponentModel.EnvironmentSettings.GetUsableSynchronizationContext();

ReaderWriterSynchronizedBase

The ReaderWriterSynchronizedBase class uses a ReaderWriterLockSlim behind the scenes (optionally one that can be explicitly set through a constructor) to do safe and synchronized read or write operations based on your requirements.

Any class that inherits from this class can try to acquire read locks:

using (this.AcquireReadLock())
{
    // ... do stuff that's read-synchronized
}

...write locks:

using (this.AcquireWriteLock())
{
    // ... do stuff that's write-synchronized
}

...or upgradeable locks (locks that may, in the future, transition from read to write locks, but are not guaranteed to do so):

using (var @lock = this.AcquireReadWriteLock())
{
    // ... do stuff that's read-synchronized

    if (shouldUpgrade)
    {
        @lock.Upgrade();

        // .. do stuff that's write-synchronized
    }
}

The locking mechanism can also be used in invocations (optionally coupled with dispose-check, as the ReaderWriterSynchronizedBase is also inheriting the DisposableBase class):

// Example taken from the InMemoryLimitedPersistedQueue<T> class.
public override int Count =>
    InvokeIfNotDisposed(
        reference => reference.ReadLock(
            referenceL2 => referenceL2.internalQueue.Count,
            reference),
        this);

The locks work like the reader/writer lock pattern:

  • Any number of read locks can coexist at the same time
  • Only one write lock can exist at the same time, and it cannot exist in parallel to any read locks
  • Only one upgradeable read lock can coexist at the same time, and its read part behaves the same as any other read lock, while the upgraded write part behaves like a write lock

The class uses the ReaderWriterLockSlim abstraction, and is fully mockable. However, implementers and inheritors of this class need to provide some access to the constructor that accepts an instance of an IReaderWriterLock?.

Data generation

The IX.DataGeneration.DataGenerator class handles most of the data generation, with self-explanatory names like RandomIntegerArray, RandomAlphaString, RandomAlphanumericStrnig, RandomLowercaseString, RandomUppercaseString RandomNumericString, RandomSymbolString, RandomString, RandomInteger or RandomNegativeInteger.

Optionally, there is a PredictableDataStore<T> class, which acts as a read-only list of items of type T, and has the capacity to generate a series of items T and optionally play them back in the exact order in which they were generated. This class usually comes in handy when doing testing with random data, in order to be able to validate that the code ran against a random but verifiable sequence of items.

Undoable

Basic concepts

The IX.Undoable namespace contains classes that are used for implementing undo and redo capabilities for inheriting objects.

The pattern used is in these classes is based on the idea of state changes. When implementing an undoable object (derived from the EditableItemBase class), two methods need to be implemented:

  • RevertChanges - this method is called to do revertions during undo
  • DoChanges - this method is called during redo

Each of these methods reveives a StateChangeBase instance, which details all the changes that need to be implemented. The developer can choose to implement all of the state changes as free as they wish, or they can use some of the already-existing classes: PropertyStateChange and PropertyStateChange<T> for signifying changes in properties, CompositeStateChange for signaling multiple changes at the same time, or SubItemStateChange, which we'll talk about later.

State changes

Whenever something happens that can be undoable, two things need to take place:

First, a call to either AdvertiseStateChange or AdvertisePropertyChange needs to happen. Within that call, the developer signals exactly what changes happen. For instance, a property that advertises its state changes would look like:

public string TestProperty
{
    get => testProperty;

    set
    {
        if (testProperty == value)
        {
            return;
        }

        AdvertisePropertyChange(
            nameof(TestProperty),
            testProperty,
            value);

        testProperty = value;

        RaisePropertyChanged(nameof(TestProperty));
    }
}

Then, you can implement both DoChanges and RevertChanges:

protected override void DoChanges(StateChangeBase stateChange)
{
    if (stateChange is PropertyStateChange<string> psts)
    {
        if (psts.PropertyName != nameof(TestProperty))
        {
            throw new InvalidOperationException(
                "Undo/Redo advertised a state change that is not for the only property, some state is leaking.");
        }

        testProperty = psts.NewValue;

        RaisePropertyChanged(nameof(TestProperty));
    }
    else
    {
        throw new InvalidOperationException(
            "Undo/Redo advertised a state change that is of a different type than property, some state is leaking.");
    }
}

protected override void RevertChanges(StateChangeBase stateChange)
{
    if (stateChange is PropertyStateChange<string> psts)
    {
        if (psts.PropertyName != nameof(TestProperty))
        {
            throw new InvalidOperationException(
                "Undo/Redo advertised a state change that is not for the only property, some state is leaking.");
        }

        testProperty = psts.OldValue;

        RaisePropertyChanged(nameof(TestProperty));
    }
    else
    {
        throw new InvalidOperationException(
            "Undo/Redo advertised a state change that is of a different type than property, some state is leaking.");
    }
}

You can implement your own state change classes, or can use the existing ones.

Undo transactions

In case there need to be multiple changes at the same time, certain classes that implement undoable patterns will use operation transactions, such as Observable classes with their StartExplicitUndoBlockTransaction method.

The easiest example for transactions comes from the unit tests for the undo capabilities of the ObservableList<T> class:

[Fact(DisplayName = "ObservableList complete explicit undo transaction block")]
public void UnitTest1()
{
    // ARRANGE
    using (var list = new ObservableList<int>(new[] { 1, 2, 3, 4, 5 }))
    {
        // ACT & ASSERT
        list.RemoveAt(0);

        Assert.Equal(
            4,
            list.Count);

        using (OperationTransaction tc = list.StartExplicitUndoBlockTransaction())
        {
            list.RemoveAt(0);
            list.RemoveAt(0);
            list.RemoveAt(0);

            tc.Success();
        }

        _ = Assert.Single(list);

        list.Undo();

        Assert.Equal(
            4,
            list.Count);

        list.Redo();

        _ = Assert.Single(list);
    }
}

Undo contexts and capturing

Undoable patterns can have multiple levels of action. The most common undoable situation is when you want to undo changes at a collection level, but, at the same time, also factor in individual items. In such a case, collection types, such as those in Observable, may use CaptureIntoUndoContext to signal to an individual item that its undo/redo capabilities will now be integrated into a parent, rather than having to handle them itself. As such, while the object itself still has to implement its own DoChanges and RevertChanges methods, its undo/redo will be managed by its patent object otherwise entirely.

CaptureIntoUndoContext is not restricted to collections, any object can capture any logical child within its undo context.

The opposite operation is ReleaseFromUndoContext, which sets the undo/redo context on the object and, from that point onwards, any undo/redo operations must be handled by the object itself.

A good example for capturing contexts and how they relate to one another is found in the unit tests for ObservableCollectionBase<T>:

public void UnitTest17()
{
    // ARRANGE
    using (var capturingList = new ObservableList<CapturedItem>(new[] { new CapturedItem() }))
    {
        // Capture into a parent context
        using (var upperCapturingList = new ObservableList<ObservableList<CapturedItem>>
                {
                    AutomaticallyCaptureSubItems = true,
                })
        {
            Assert.Null(capturingList.ParentUndoContext);
            upperCapturingList.Add(capturingList);
            Assert.Equal(
                upperCapturingList,
                capturingList.ParentUndoContext);

            Assert.Null(capturingList[0].ParentUndoContext);

            // ACT
            capturingList.AutomaticallyCaptureSubItems = true;

            // ASSERT
            Assert.Equal(
                capturingList,
                capturingList[0].ParentUndoContext);
        }
    }
}

You will notice, apart from how the contexts get translated in captured and logical parent objects, that some implementations might even do automatic capturing, such as the ObservableList<T> that is pictured in the unit test, with its AutomaticallyCaptureSubItems property.

The same unit tests class located here offer further examples on how to properly use undo contexts and transactions.

Observable

( 🚧 under construction 🚧 )

Contracts

( 🚧 under construction 🚧 )

Extensions

( 🚧 under construction 🚧 )

ForEach on an IEnumerable.

Given we have:

IEnumerable<someClass> someCollection;

We would call a method for each item of the collection like this:

foreach (var item in someCollection)
{
    someMethod(item);
}

With the extension method, we could call it like this:

someCollection.ForEach(someMethod);

The same would hold true for an array.

Although, to be fair, if you're going to have a benchmark of:

i++;

...then the foreach cycle will be faster, since you will not have an extra method invocation.

As an extra bonus, you can run them using task parallel library (.NET Standard 1.1 and above only).

someCollection.ParallelForEach(someMethod);

Sequence Equals

The next example comes from the need to compare data. Comparison on arrays or enumerables (or between an array or an IEnumerable) has always been slightly burdensome. We have a helper for that:

if (someCollection.SequenceEquals(someOtherCollection))
{
    // Do something
}

Component model extensions

( 🚧 under construction 🚧 )

Efficiency extensions

( 🚧 under construction 🚧 )

Event model extensions

( 🚧 under construction 🚧 )

Globalization extensions

( 🚧 under construction 🚧 )

Threading extensions

( 🚧 under construction 🚧 )

Special collections

( 🚧 under construction 🚧 )

Threading and interlocking extensions

( 🚧 under construction 🚧 )

Document metadata

Last update: 11th October, 2022