Click here to Skip to main content
15,894,410 members
Articles / Programming Languages / C#

Tweaked Events

Rate me:
Please Sign up or sign in to vote.
4.83/5 (12 votes)
18 Dec 2010MIT18 min read 34.8K   289   41  
Framework for customizing events. Comes with Weak Events and Synced Events
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.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior) ThoughtWorks
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions