#region License
/**
* Ingenious MVC : An MVC framework for .NET 2.0
* Copyright (C) 2006, JDP Group
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: - Kent Boogaart (kentcb@internode.on.net)
*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace Ingenious.Mvc.Util{
/// <summary>
/// Provides helper methods for raising events.
/// </summary>
/// <remarks>
/// <para>
/// The <c>EventHelper</c> class provides methods that can be used to raise events. It avoids the need for explicitly
/// checking event sinks for <c>null</c> before raising the event.
/// </para>
/// </remarks>
/// <example>
/// The following example shows how a non-generic event can be raised:
/// <code>
/// public event EventHandler Changed;
///
/// protected void OnChanged()
/// {
/// EventHelper.Raise(Changed, this);
/// }
/// </code>
/// </example>
/// <example>
/// The following example shows how a non-generic event can be raised where the event type requires a specific
/// <c>EventArgs</c> subclass:
/// <code>
/// public event PropertyChangedEventHandler PropertyChanged;
///
/// protected void OnPropertyChanged(PropertyChangedEventArgs e)
/// {
/// EventHelper.Raise(PropertyChanged, this, e);
/// }
/// </code>
/// </example>
/// <example>
/// The following example shows how a generic event can be raised:
/// <code>
/// public event EventHandler<EventArgs> Changed;
///
/// protected void OnChanged()
/// {
/// EventHelper.Raise(Changed, this, EventArgs.Empty);
/// }
/// </code>
/// </example>
/// <example>
/// The following example shows how a generic event with custom event arguments can be raised:
/// <code>
/// public event EventHandler<MyEventArgs> MyEvent;
///
/// protected void OnMyEvent(MyEventArgs e)
/// {
/// EventHelper.Raise(MyEventArgs, this, e);
/// }
/// </code>
/// </example>
/// <example>
/// The following example raises a generic event, but does not create the event arguments unless there is at least one
/// handler for the event:
/// <code>
/// public event EventHandler<MyEventArgs> MyEvent;
///
/// protected void OnMyEvent(int someData)
/// {
/// EventHelper.Raise(MyEvent, this, delegate
/// {
/// return new MyEventArgs(someData);
/// });
/// }
/// </code>
/// </example>
public static class EventHelper
{
private static readonly Log _log = Log.CreateForType(typeof(EventHelper));
/// <summary>
/// Raises a non-generic event.
/// </summary>
/// <remarks>
/// This method raises the specified non-generic event and passes in <c>EventArgs.Empty</c> as the event arguments.
/// </remarks>
/// <param name="handler">
/// The event to be raised.
/// </param>
/// <param name="sender">
/// The sender of the event.
/// </param>
[SuppressMessage("Microsoft.Design", "CA1030", Justification = "False positive - the Raise method overloads are supposed to raise an event on behalf of a client, not on behalf of its declaring class.")]
[DebuggerHidden]
public static void Raise(EventHandler handler, object sender)
{
if (handler != null)
{
#if DEBUG
RaiseWithDiagnostics(handler, sender, EventArgs.Empty);
#else
handler(sender, EventArgs.Empty);
#endif
}
}
/// <summary>
/// Raises a non-generic event.
/// </summary>
/// <remarks>
/// This method can be used to raise a non-generic event that needs a specific <see cref="EventArgs"/> subclass as its
/// second parameter. This method assumes that <paramref name="handler"/> points to a method that conforms to the
/// standard .NET event signature. That is, it takes an <see cref="object"/> as its first parameter and an
/// <see cref="EventArgs"/> instance as its second.
/// </remarks>
/// <param name="handler">
/// The event handler.
/// </param>
/// <param name="sender">
/// The sender of the event.
/// </param>
/// <param name="e">
/// The arguments for the event.
/// </param>
/// <exception cref="TargetParameterCountException">
/// If <paramref name="handler"/> does not contain the correct number of arguments or contains arguments of the wrong
/// type or in the wrong order.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1030", Justification = "False positive - the Raise method overloads are supposed to raise an event on behalf of a client, not on behalf of its declaring class.")]
[DebuggerHidden]
public static void Raise(Delegate handler, object sender, EventArgs e)
{
if (handler != null)
{
#if DEBUG
RaiseWithDiagnostics(handler, sender, e);
#else
handler.DynamicInvoke(sender, e);
#endif
}
}
/// <summary>
/// Raises a generic event.
/// </summary>
/// <remarks>
/// This method raises a generic event, passing in the specified event arguments.
/// </remarks>
/// <typeparam name="T">
/// The event arguments type.
/// </typeparam>
/// <param name="handler">
/// The event to be raised.
/// </param>
/// <param name="sender">
/// The sender of the event.
/// </param>
/// <param name="e">
/// The arguments for the event.
/// </param>
[SuppressMessage("Microsoft.Design", "CA1030", Justification = "False positive - the Raise method overloads are supposed to raise an event on behalf of a client, not on behalf of its declaring class.")]
[DebuggerHidden]
public static void Raise<T>(EventHandler<T> handler, object sender, T e)
where T : EventArgs
{
if (handler != null)
{
#if DEBUG
RaiseWithDiagnostics(handler, sender, e);
#else
handler(sender, e);
#endif
}
}
/// <summary>
/// Raises a generic event, but does not create the event arguments unless there is at least one handler for the event.
/// </summary>
/// <typeparam name="T">
/// The event arguments type.
/// </typeparam>
/// <param name="handler">
/// The event to be raised.
/// </param>
/// <param name="sender">
/// The sender of the event.
/// </param>
/// <param name="createEventArguments">
/// The delegate to invoke when an event arguments instance is needed.
/// </param>
/// <exception cref="ArgumentNullException">
/// If <paramref name="createEventArguments"/> is <see langword="null"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1030", Justification = "False positive - the Raise method overloads are supposed to raise an event on behalf of a client, not on behalf of its declaring class.")]
[DebuggerHidden]
public static void Raise<T>(EventHandler<T> handler, object sender, CreateEventArguments<T> createEventArguments)
where T : EventArgs
{
ArgumentHelper.AssertNotNull(createEventArguments, "createEventArguments");
if (handler != null)
{
#if DEBUG
RaiseWithDiagnostics(handler, sender, createEventArguments());
#else
handler(sender, createEventArguments());
#endif
}
}
#if DEBUG
/// <summary>
/// A method used by debug builds to raise events and log diagnostic information in the process.
/// </summary>
/// <remarks>
/// This method is only called in debug builds. It logs details about raised events and any exceptions thrown by event
/// handlers.
/// </remarks>
/// <param name="handler">
/// The event handler.
/// </param>
/// <param name="parameters">
/// Parameters to the event handler.
/// </param>
private static void RaiseWithDiagnostics(Delegate handler, params object[] parameters)
{
Debug.Assert(handler != null);
string threadName = System.Threading.Thread.CurrentThread.Name;
_log.Debug("Event being raised by thread '{0}'.", threadName);
foreach (Delegate del in handler.GetInvocationList())
{
try
{
_log.Debug(" Calling method '{0}.{1}'.", del.Method.DeclaringType.FullName, del.Method.Name);
del.DynamicInvoke(parameters);
}
catch (Exception ex)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" An exception occurred in the event handler:");
while (ex != null)
{
sb.Append(" ").AppendLine(ex.Message);
sb.AppendLine(ex.StackTrace);
ex = ex.InnerException;
if (ex != null)
{
sb.AppendLine("--- INNER EXCEPTION ---");
}
}
_log.Debug(sb.ToString());
//the exception isn't swallowed - just logged
throw;
}
}
_log.Debug("Finished raising event by thread '{0}'.", threadName);
}
#endif
/// <summary>
/// A handler used to create an event arguments instance for the
/// <see cref="Raise<T>(EventHandler<T>, object, CreateEventArguments<T>)"/> method.
/// </summary>
/// <remarks>
/// This delegate is invoked by the
/// <see cref="Raise<T>(EventHandler<T>, object, CreateEventArguments<T>)"/> method to create the
/// event arguments instance. The handler should create the instance and return it.
/// </remarks>
/// <typeparam name="T">
/// The event arguments type.
/// </typeparam>
/// <returns>
/// The event arguments instance.
/// </returns>
public delegate T CreateEventArguments<T>()
where T : EventArgs;
}
}
|