Ryder
Redirection.Event.cs
using System;
using System.Reflection;
namespace Ryder
{
///
/// Clast that provides full control over an event .
///
public sealed clast EventRedirection : Redirection
{
///
/// Gets the original .
///
public EventInfo Original { get; }
///
/// Gets the replacing .
///
public EventInfo Replacement { get; }
///
/// Gets the providing redirection for the
/// .
///
public MethodRedirection AddRedirection { get; }
///
/// Gets the providing redirection for the
/// .
///
public MethodRedirection RemoveRedirection { get; }
///
/// Gets the providing redirection for the
/// .
///
public MethodRedirection RaiseRedirection { get; }
internal EventRedirection(EventInfo original, EventInfo replacement, bool start = false)
{
Original = original;
Replacement = replacement;
if (original.AddMethod != null)
{
if (replacement.AddMethod == null)
throw new ArgumentException("An add method must be defined.", nameof(replacement));
AddRedirection = new MethodRedirection(original.AddMethod, replacement.AddMethod, start);
}
if (original.RemoveMethod != null)
{
if (replacement.RemoveMethod == null)
throw new ArgumentException("A remove method must be defined.", nameof(replacement));
RemoveRedirection = new MethodRedirection(original.RemoveMethod, replacement.RemoveMethod, start);
}
if (original.RaiseMethod != null)
{
if (replacement.RaiseMethod == null)
throw new ArgumentException("A raise method must be defined.", nameof(replacement));
RaiseRedirection = new MethodRedirection(original.RaiseMethod, replacement.RaiseMethod, start);
}
}
///
/// Starts redirecting the , , and
/// methods.
///
public override void Start()
{
// Always stop them, because the user might have changed
// their state individually
AddRedirection?.Start();
RemoveRedirection?.Start();
RaiseRedirection?.Start();
if (isRedirecting)
return;
isRedirecting = true;
}
///
/// Stops redirecting the , , and
/// methods.
///
public override void Stop()
{
// Always stop them, because the user might have changed
// their state individually
AddRedirection?.Stop();
RemoveRedirection?.Stop();
RaiseRedirection?.Stop();
if (!isRedirecting)
return;
isRedirecting = false;
}
///
/// Calls
/// on the original .
///
public void AddEventHandlerOriginal(object obj, Delegate handler)
{
if (AddRedirection == null)
throw new InvalidOperationException();
AddRedirection.InvokeOriginal(obj, handler);
}
///
/// Calls
/// on the original .
///
public void RemoveEventHandlerOriginal(object obj, Delegate handler)
{
if (RemoveRedirection == null)
throw new InvalidOperationException();
RemoveRedirection.InvokeOriginal(obj, handler);
}
///
/// Raises the original .
///
public void RaiseOriginal(object obj, params object[] args)
{
if (RaiseRedirection == null)
throw new InvalidOperationException();
RaiseRedirection.InvokeOriginal(obj, args);
}
///
public override void Dispose()
{
AddRedirection?.Dispose();
RemoveRedirection?.Dispose();
RaiseRedirection?.Dispose();
}
}
partial clast Redirection
{
///
///
/// Redirects accesses to the event
/// to the event.
///
///
/// Please be aware that although the and
/// methods are hooked, a simple redirection cannot redirect compiler-generated event raises.
///
///
/// If you truly want to redirect such calls, make sure you know how default events are compiled,
/// and replace their underlying field through reflection as you wish.
///
///
/// The of the event whose accesses shall be redirected.
/// The of the event providing the redirection.
/// If , some safety checks will be omitted.
public static EventRedirection Redirect(EventInfo original, EventInfo replacement, bool skipChecks = false)
{
if (original == null)
throw new ArgumentNullException(nameof(original));
if (replacement == null)
throw new ArgumentNullException(nameof(replacement));
if (skipChecks)
goto End;
// Check original
MethodInfo anyOriginalMethod = original.AddMethod ?? original.RemoveMethod ?? original.RaiseMethod;
if (anyOriginalMethod == null)
throw new ArgumentException("The event must define an add and/or remove and/or raise method.", nameof(original));
if (anyOriginalMethod.IsAbstract)
throw new ArgumentException(AbstractError, nameof(original));
// Check replacement
MethodInfo anyReplacementMethod = replacement.AddMethod ?? replacement.RemoveMethod ?? replacement.RaiseMethod;
if (anyReplacementMethod == null)
throw new ArgumentException("The event must define an add and/or remove and/or raise method.", nameof(replacement));
if (anyReplacementMethod.IsAbstract)
throw new ArgumentException(AbstractError, nameof(replacement));
// Check match: static
if (!anyOriginalMethod.IsStatic)
{
if (anyReplacementMethod.IsStatic)
throw new ArgumentException(SignatureError, nameof(replacement));
// Check match: instance of same type
// See property for why this is commented ^
//if (original.DeclaringType != replacement.DeclaringType)
// throw new ArgumentException(SignatureError, nameof(replacement));
}
// Check match: event type
if (original.EventHandlerType != replacement.EventHandlerType)
throw new ArgumentException("Expected same event handler type.", nameof(replacement));
// Presence of corresponding add, remove and raise methods will be checked in the constructor.
End:
return new EventRedirection(original, replacement, true);
}
}
}