/*
DynamicMethodDispatcher and MuticastDynamicMethodDispatcher classes:
Generic class DynamicMethodDispatcher dispatches a call to a generic delegate isntance found by a dictionary key
Generic class MuticastDynamicMethodDispatcher is a multi-case version of DynamicMethodDispatcher.
Copyright (C) 2006-2011 by Sergey A Kryukov
http://www.SAKryukov.org
*/
namespace SA.Universal.Technology {
using System.Collections.Generic;
/// <summary>
/// Exception type used in DynamicMethodDispatcher and MuticastDynamicMethodDispatcher
/// when a KEY key is not found in the dictionary of delegates
/// </summary>
/// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
/// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
public class DynamicMethodNotFoundException<KEY, MESSAGE> : System.Exception {
internal DynamicMethodNotFoundException(KEY key, MESSAGE message) { this.FKey = key; this.FMessage = message; }
public KEY KeyValue { get { return this.FKey; } }
public MESSAGE MessageValue { get { return this.FMessage; } }
KEY FKey; MESSAGE FMessage;
} //class DynamicMethodNotFoundException
/// <summary>
/// Base class for DynamicMethodDispatcher and MuticastDynamicMethodDispatcher
/// </summary>
/// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
/// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
/// <typeparam name="RETURN">Return type expected in return of the Invoke methods of Dispatcher</typeparam>
public abstract class DynamicMethodDispatcherBase<KEY, MESSAGE, RETURN> {
/// <summary>
/// Delegate type used for dynamic method dispartching
/// </summary>
/// <param name="key">Key of the delegate dictionary</param>
/// <param name="message">Message of the message passed to Invoke methods of Dispatcher</param>
/// <returns>RETURN</returns>
public delegate RETURN DynamicMethod(KEY key, MESSAGE message);
/// <summary>
/// Indexes returning length of invocation list for a key
/// </summary>
/// <param name="key">Key key used to check up the length of invocation list</param>
/// <returns>Returns 0 is key is not found, otherwise the length of invocation list</returns>
public int this[KEY key] {
get {
DynamicMethod method;
if (Dictionary.TryGetValue(key, out method))
return method.GetInvocationList().Length;
return 0;
} //get this
} //this
/// <summary>
/// Property returns all keys in Dynamic Method dictionary
/// </summary>
/// <returns>Array of KEY; all keys in Dynamic Method dictionary</returns>
KEY[] Keys {
get {
KEY[] result = new KEY[Dictionary.Keys.Count];
Dictionary.Keys.CopyTo(result, 0);
return result;
} //get Keys
} //Keys
#region implementation
internal protected RETURN SingleCastInvoke(KEY key, MESSAGE message) {
DynamicMethod method;
if (Dictionary.TryGetValue(key, out method))
return method.Invoke(key, message);
throw new DynamicMethodNotFoundException<KEY, MESSAGE>(key, message);
} //SingleCastInvoke
internal protected bool SingleCastTryInvoke(KEY key, MESSAGE message, out RETURN returnValue) {
DynamicMethod method;
bool success = Dictionary.TryGetValue(key, out method);
if (success)
returnValue = method.Invoke(key, message);
else
returnValue = default(RETURN);
return success;
} //SingleCastTryInvoke
internal protected Dictionary<KEY, DynamicMethod> Dictionary = new Dictionary<KEY, DynamicMethod>();
#endregion implementation
} //DynamicMethodDispatcherBase
/// <summary>
/// Dispatcher type which allows only no more than one dynamic method per key
/// </summary>
/// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
/// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
/// <typeparam name="RETURN">Return type expected in return of the Invoke methods of Dispatcher</typeparam>
public class DynamicMethodDispatcher<KEY, MESSAGE, RETURN> : DynamicMethodDispatcherBase<KEY, MESSAGE, RETURN> {
/// <summary>
/// Adds a dynamic method for a key if the key is not found in the invocation dictionary
/// </summary>
/// <param name="key">Key for the invocation dictionary</param>
/// <param name="method">Dynamic method</param>
/// <returns>Returns true a method is successfully added; false means the key is already found</returns>
public bool Add(KEY key, DynamicMethod method) {
if (Dictionary.ContainsKey(key)) return false;
Dictionary.Add(key, method);
return true;
} //AddReplace
/// <summary>
/// This invocation method throws exception if key is not found
/// </summary>
public RETURN Invoke(KEY key, MESSAGE message) { return SingleCastInvoke(key, message); }
/// <summary>
/// This invocation method does not throw exception if key is not found, returns false instead
/// </summary>
public bool TryInvoke(KEY key, MESSAGE message, out RETURN returnValue) { return SingleCastTryInvoke(key, message, out returnValue); }
} //class DynamicMethodDispatcher
/// <summary>
/// Multicast variant of DynamicMethodDispatcher
/// </summary>
/// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
/// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
/// <typeparam name="RETURN">Return type expected in return of the Invoke methods of Dispatcher</typeparam>
public class MuticastDynamicMethodDispatcher<KEY, MESSAGE, RETURN> : DynamicMethodDispatcherBase<KEY, MESSAGE, RETURN> {
/// <summary>
/// Accumulation method used to return Invocation result accumulated from all methods in the invocation list
/// </summary>
/// <param name="index">Index of the call in invocation list</param>
/// <param name="key">Dictionary key of the delegate instance being invoked</param>
/// <param name="message">Message passed during delegate invocation</param>
/// <param name="lastResultAvailable">True is the last result returned from the previous call to the accumulation method is available</param>
/// <param name="lastResult">Last result returned from the previous call to the accumulation method</param>
/// <param name="currentResult">Currect result returned from the method of the invocation list</param>
/// <returns>Returns accumulated result after the call to a member of the invocation list</returns>
public delegate RETURN AccumulationMethod(
int index,
KEY key, MESSAGE message,
bool lastResultAvailable, RETURN lastResult, RETURN currentResult);
/// <summary>
/// Adds a dinamic method by a dictionary key to the available invocation list or creates new delegate instance
/// and adds it to invocation dictionary
/// </summary>
public void Add(KEY key, DynamicMethod method) {
DynamicMethod delegateInstance;
if (Dictionary.TryGetValue(key, out delegateInstance)) {
Dictionary.Remove(key); //adding method to invokation list will change delegate's identity (wow!):
delegateInstance += method;
} else
delegateInstance = method;
Dictionary.Add(key, delegateInstance);
} //AddReplace
/// <summary>
/// This invocation method does not cause exception if a key is not found but returns null
/// </summary>
/// <returns>Returns null if a key is not found, otherwise an array of all return values
/// returned from every member of invocation list</returns>
public RETURN[] Invoke(KEY key, MESSAGE message) {
DynamicMethod delegateInstance;
if (Dictionary.TryGetValue(key, out delegateInstance)) {
System.Delegate[] invocationList = delegateInstance.GetInvocationList();
int index = 0;
RETURN[] returnValue = new RETURN[invocationList.Length];
foreach (System.Delegate method in invocationList) {
returnValue[index] = ((DynamicMethod)method)(key, message);
index++;
} //loop invocationList
return returnValue;
} else
return null;
} //Invoke
/// <summary>
/// This invocation method uses <seealso cref="AccumulationMethod"></seealso>
/// to accumulate all values returned by all members of invocation list in one value;
/// this method can throw exception is key is not found />
/// </summary>
/// <returns>Accumulated result of invocation, depending on invocation list and accumulation method</returns>
public RETURN Invoke(KEY key, MESSAGE message, AccumulationMethod accumulation) {
bool dummyFound;
return AccumulatedInvoke(key, message, accumulation, true, out dummyFound);
} //Invoke
/// <summary>
/// This method is a "try" variant of invocation method using Accumulation method,
/// it does not throw exception but return success status
/// </summary>
/// <param name="returnValue">Accumulated result of invocation, depending on invocation list and accumulation method</param>
/// <returns>True is invocation was successful, false if key is not found</returns>
public bool TryInvoke(KEY key, MESSAGE message, AccumulationMethod accumulation, out RETURN returnValue) {
bool found;
returnValue = AccumulatedInvoke(key, message, accumulation, false, out found);
return found;
} //Invoke
/// <summary>
/// This invocation method ignore results returned by the members of invocation list during invocaion;
/// it can throw exception if key is not found
/// </summary>
void InvokeNoReturn(KEY key, MESSAGE message) {
SingleCastInvoke(key, message);
} //InvokeNoReturn
/// <summary>
/// This is a "try" variant of <seealso cref="InvokeNoReturn"/>; it does not throw exception if key is not found
/// </summary>
/// <returns>True is invocation was successful, false if key is not found</returns>
public bool TryInvokeNoReturn(KEY key, MESSAGE message) {
RETURN dummyReturnValue;
return SingleCastTryInvoke(key, message, out dummyReturnValue);
} //TryInvokeNoReturn
#region implementation
public RETURN AccumulatedInvoke(KEY key, MESSAGE message, AccumulationMethod accumulation, bool throwException, out bool found) {
if (accumulation == null) { //fallback:
if (throwException) {
found = true;
InvokeNoReturn(key, message);
} else
found = TryInvokeNoReturn(key, message);
return default(RETURN);
} //if no accumulation method
DynamicMethod delegateInstance;
found = Dictionary.TryGetValue(key, out delegateInstance);
if (found) {
System.Delegate[] invocationList = delegateInstance.GetInvocationList();
int index = 0;
RETURN lastResult = default(RETURN);
foreach (System.Delegate method in invocationList) {
bool lastResultAvailable = index > 0;
RETURN currentResult = ((DynamicMethod)method)(key, message);
lastResult = accumulation(index, key, message, lastResultAvailable, lastResult, currentResult);
index++;
} //loop invocationList
return lastResult;
} else
if (throwException)
throw new DynamicMethodNotFoundException<KEY, MESSAGE>(key, message);
else
return default(RETURN);
} //AccumulatedInvoke
#endregion implementation
} //class MuticastDynamicMethodDispatcher
} //namespace SA.Universal.Technology