MulticastExceptionHandler.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. using System.Collections.Generic;
  2. using System.Threading.Tasks;
  3. using System;
  4. namespace HandyControl.Interactivity.Commands
  5. {
  6. #nullable enable
  7. /// <summary>
  8. /// Provides a wrapper for managing multicast delegates for handling specific errors
  9. /// </summary>
  10. public struct MulticastExceptionHandler
  11. {
  12. private readonly Dictionary<Type, MulticastDelegate> _handlers;
  13. /// <summary>
  14. /// Initializes a new MulticastExceptionHandler
  15. /// </summary>
  16. public MulticastExceptionHandler()
  17. {
  18. _handlers = new Dictionary<Type, MulticastDelegate>();
  19. }
  20. public void Register<TException>(MulticastDelegate callback)
  21. where TException : Exception
  22. {
  23. _handlers.Add(typeof(TException), callback);
  24. }
  25. /// <summary>
  26. /// Determines if there is a callback registered to handle the specified exception
  27. /// </summary>
  28. /// <param name="exception">An <see cref="Exception"/> to handle or rethrow</param>
  29. /// <returns><c>True</c> if a Callback has been registered for the given type of <see cref="Exception"/>.</returns>
  30. public bool CanHandle(Exception exception) =>
  31. GetDelegate(exception.GetType()) is not null;
  32. /// <summary>
  33. /// Handles a specified
  34. /// </summary>
  35. /// <param name="exception"></param>
  36. /// <param name="parameter"></param>
  37. public async void Handle(Exception exception, object? parameter = null) =>
  38. await HandleAsync(exception, parameter);
  39. public async Task HandleAsync(Exception exception, object? parameter = null)
  40. {
  41. var multicastDelegate = GetDelegate(exception.GetType());
  42. if (multicastDelegate is null)
  43. return;
  44. // Get Invoke() method of the delegate
  45. var invokeMethod = multicastDelegate.GetType().GetMethod("Invoke");
  46. if (invokeMethod == null)
  47. throw new InvalidOperationException($"Could not find Invoke() method for delegate of type {multicastDelegate.GetType().Name}");
  48. var parameters = invokeMethod.GetParameters();
  49. var arguments = parameters.Length switch
  50. {
  51. 0 => Array.Empty<object?>(),
  52. 1 => typeof(Exception).IsAssignableFrom(parameters[0].ParameterType) ? new object?[] { exception } : new object?[] { parameter },
  53. 2 => typeof(Exception).IsAssignableFrom(parameters[0].ParameterType) ? new object?[] { exception, parameter } : new object?[] { parameter, exception },
  54. _ => throw new InvalidOperationException($"Handler of type {multicastDelegate.GetType().Name} is not supported", exception)
  55. };
  56. // Invoke the delegate
  57. var result = invokeMethod.Invoke(multicastDelegate, arguments);
  58. // If the handler is async (returns a Task), then we await the task
  59. if (result is Task task)
  60. {
  61. await task;
  62. }
  63. #if NET6_0_OR_GREATER
  64. else if (result is ValueTask valueTask)
  65. {
  66. await valueTask;
  67. }
  68. #endif
  69. }
  70. private MulticastDelegate? GetDelegate(Type type)
  71. {
  72. if (_handlers.ContainsKey(type))
  73. return _handlers[type];
  74. else if (type.BaseType is not null)
  75. return GetDelegate(type.BaseType);
  76. return null;
  77. }
  78. }
  79. }