|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace JpLabs.TweakedEvents
{
[ImmutableObject(true)] //REMARK: it should behave as it was immutable, but it isn't 100% immutable
public abstract class TweakedEvent<TEH> : ITweakedEvent<TEH> where TEH : class
{
// Static methods / members
public static readonly IEventEntry<TEH>[] EmptyEntryArray = ImmutableListExt.EmptyArray<IEventEntry<TEH>>.Instance;
static TweakedEvent()
{
GenericArgumentValidator.ValidateEventHandler<TEH>();
}
// Members
protected IEventEntry<TEH>[] EventEntries { get; private set; }
// Contructors
public TweakedEvent()
{
EventEntries = EmptyEntryArray;
}
protected TweakedEvent(IEventEntry<TEH>[] initialEntries)
{
EventEntries = initialEntries ?? EmptyEntryArray;
}
// Abstract Methods
public abstract IEventEntry<TEH> CreateEntry(TEH handler);
protected abstract ITweakedEvent<TEH> CreateNew(IEventEntry<TEH>[] entries);
// Public Virtual Methods
public virtual ITweakedEvent<TEH> Combine(TEH handler)
{
if (handler == null) return this;
var handlerAsDelegate = (Delegate)(object)handler;
IEventEntry<TEH> eventEntry
= (handlerAsDelegate.Target is IEventEntry<TEH>)
? (IEventEntry<TEH>)handlerAsDelegate.Target //If delegate points to an event entry => unpack event entry
: CreateEntry(handler); //Else => Create an entry from the delegate
return Combine(eventEntry);
}
public virtual ITweakedEvent<TEH> Subtract(TEH handler)
{
if (handler == null) return this;
var handlerAsDelegate = (Delegate)(object)handler;
if (handlerAsDelegate.Target is IEventEntry<TEH>) { //If delegate points to an event entry => unpack event entry
IEventEntry<TEH> eventEntry = (IEventEntry<TEH>)handlerAsDelegate.Target;
return Subtract(eventEntry);
}
//Search for the LAST event entry that matches with the HANDLER provided
var entries = EventEntries;
int last = entries.Length - 1;
for (int i = last; i >= 0; i--) {
if (entries[i].EqualsHandler(handler)) {
return CreateNew(entries.ImutRemoveAt(i));
}
}
return this; //handler not found => return self
}
public virtual ITweakedEvent<TEH> Combine(IEventEntry<TEH> entry)
{
if (entry == null) return this;
return CreateNew(EventEntries.ImutAdd(entry));
}
public virtual ITweakedEvent<TEH> Subtract(IEventEntry<TEH> entry)
{
if (entry == null) return this;
//Search for the LAST event entry that matches with the ENTRY provided
var entries = EventEntries;
int last = entries.Length - 1;
for (int i = last; i >= 0; i--) {
if (entries[i].Equals(entry)) {
return CreateNew(entries.ImutRemoveAt(i));
}
}
return this; //handler not found => return self
}
//Public Methods
public bool IsEmpty()
{
return EventEntries.Length == 0;
}
public ITweakedEvent<TEH> Clear()
{
return CreateNew(EventEntries.ImutClear());
}
void ITweakedEvent.Raise(object sender, EventArgs e)
{
//1. Get a reference to the current list of entries in order to avoid synchronization issues
var entries = EventEntries;
//2. Invoke each entry
bool needsCleanup = false;
int count = entries.Length;
for (int i=0; i<count; i++) {
needsCleanup |= entries[i].Invoke(sender, e);
}
//3. If a cleanup is needed, do it
if (needsCleanup) RemoveReleasedEntries();
}
protected virtual void RemoveReleasedEntries()
{
//TODO: REVIEW: Shouldn't this clean up run asynchronously?
//TODO: REVIEW: Although this is an immutable class, there is a place where it's updated: here
this.EventEntries = GetNotDisposedOnly(this.EventEntries);
}
protected static IEventEntry<TEH>[] GetNotDisposedOnly(IEventEntry<TEH>[] entries)
{
return entries.Where( entry => !entry.IsDisposed ).ToArray();
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.