大家好我是juster,GeneralUpdate(一款应用程序自动升级组件)作者。在设计组件的时候也会遇到不少问题,经常会在半夜想到一个解决办法爬起来将这个办法或者是设计记录下来或者直接实现。这里将很久之前设计思路写出来向大家讨教、分享,在设计和实现的过程遇到的问题以及是如何解决的。
下面这张图是用来描述在日常编码中我们编写代码的场景,像极了一颗“洋葱”。当异常逐层向外层传递时会受到一些影响。
这个时候可能会想到,不断向外层传递异常信息的时候会有这些问题。如果集中将异常管理起来,点对点抛到最外层不就可以解决问题了吗?
确实,这样做简单明了但是光有解决思路不能落实成解决方案那么也只是产生了新的问题罢了。
软件工程中流传着一句话,大部分软件问题通过增加一层就能解决,如果一层解决不了那就两层。
于是有了以下代码:
/// <summary>
/// Manage all events in the component.
/// </summary>
public class EventManager : IEventManager, IDisposable
{
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private static extern Boolean CloseHandle(IntPtr handle);
private static readonly object _lockObj = new object();
private static EventManager _instance;
private Dictionary<Type, Delegate> _dicDelegates = new Dictionary<Type, Delegate>();
// Track whether Dispose has been called.
private bool disposed = false;
// Pointer to an external unmanaged resource.
private IntPtr handle;
// Other managed resource this class uses.
private Component component = null;
private EventManager() => component = new Component();
// Use C# finalizer syntax for finalization code.
// This finalizer will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide finalizer in types derived from this class.
~EventManager()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(disposing: false) is optimal in terms of
// readability and maintainability.
Dispose(disposing: false);
}
public static EventManager Instance
{
get
{
if (_instance == null)
{
lock (_lockObj)
{
if (_instance == null)
_instance = new EventManager();
}
}
return _instance;
}
}
/// <summary>
/// Add listener
/// </summary>
/// <typeparam name="TDelegate">Specify the delegate type.</typeparam>
/// <param name="newDelegate">Delegate to be added.</param>
/// <exception cref="ArgumentNullException">parameter null exception.</exception>
public void AddListener<TDelegate>(TDelegate newDelegate) where TDelegate : Delegate
{
if (newDelegate == null) throw new ArgumentNullException(nameof(newDelegate));
if (_dicDelegates.ContainsKey(typeof(TDelegate))) return;
handle = new IntPtr(1);
_dicDelegates.Add(typeof(TDelegate), newDelegate);
}
/// <summary>
/// Remove listener
/// </summary>
/// <typeparam name="TDelegate">Specify the delegate type.</typeparam>
/// <param name="oldDelegate">Remove old delegates.</param>
/// <exception cref="ArgumentNullException">parameter null exception.</exception>
public void RemoveListener<TDelegate>(TDelegate oldDelegate) where TDelegate : Delegate
{
if (oldDelegate == null) throw new ArgumentNullException(nameof(oldDelegate));
var delegateType = oldDelegate.GetType();
if (!delegateType.IsInstanceOfType(typeof(TDelegate))) return;
Delegate tempDelegate = null;
if (_dicDelegates.TryGetValue(delegateType, out tempDelegate))
{
if (tempDelegate == null)
{
_dicDelegates.Remove(delegateType);
}
else
{
_dicDelegates[delegateType] = tempDelegate;
}
}
}
/// <summary>
/// Triggers a delegate of the same type.
/// </summary>
/// <typeparam name="TDelegate"></typeparam>
/// <param name="sender">trigger source object.</param>
/// <param name="eventArgs">event args.</param>
/// <exception cref="ArgumentNullException">parameter null exception.</exception>
public void Dispatch<TDelegate>(object sender, EventArgs eventArgs) where TDelegate : Delegate
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
if (eventArgs == null) throw new ArgumentNullException(nameof(eventArgs));
if (!_dicDelegates.ContainsKey(typeof(TDelegate))) return;
_dicDelegates[typeof(TDelegate)].DynamicInvoke(sender, eventArgs);
}
/// <summary>
/// Clear all listeners.
/// </summary>
public void Clear() => _dicDelegates.Clear();
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(disposing: true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SuppressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note disposing has been done.
disposed = true;
}
}
}
/// <summary>
/// Event manager interface.
/// </summary>
public interface IEventManager
{
/// <summary>
/// Adding Event Listeners.
/// </summary>
/// <typeparam name="TDelegate">Generic delegate.</typeparam>
/// <param name="newDelegate">New delegate that needs to be injected.</param>
void AddListener<TDelegate>(TDelegate newDelegate) where TDelegate : Delegate;
/// <summary>
/// Removing Event Listening.
/// </summary>
/// <typeparam name="TDelegate">Generic delegate.</typeparam>
/// <param name="oldDelegate">Need to remove an existing delegate.</param>
void RemoveListener<TDelegate>(TDelegate oldDelegate) where TDelegate : Delegate;
/// <summary>
/// Triggers notifications of the same event type based on the listening event type.
/// </summary>
/// <typeparam name="TDelegate">generic delegate.</typeparam>
/// <param name="sender">Event handler.</param>
/// <param name="eventArgs">Event args.</param>
void Dispatch<TDelegate>(object sender, EventArgs eventArgs) where TDelegate : Delegate;
/// <summary>
/// Remove all injected delegates.
/// </summary>
void Clear();
}
//抛出异常
EventManager.Instance.Dispatch<Action<object, ExceptionEventArgs>>(this, new ExceptionEventArgs(ex));
//接收异常
EventManager.Instance.AddListener((sender,exception)=>{ ...... });