using System.Reactive.Disposables; namespace Toolkit.Foundation; public class Subscription(SubscriptionCollection subscriptions, IDisposer disposer) : ISubscription { public void Add(object subscriber) { Type handlerType = subscriber.GetType(); object? key = GetKeyFromHandler(subscriber); foreach (Type interfaceType in GetHandlerInterfaces(handlerType)) { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{argumentType}"; subscriptions.AddOrUpdate(subscriptionKey, _ => new List { new(subscriber) }, (_, collection) => { collection.Add(new WeakReference(subscriber)); return collection; }); disposer.Add(subscriber, Disposable.Create(() => RemoveSubscriber(subscriber, argumentType))); } } } public void Remove(object subscriber) { Type handlerType = subscriber.GetType(); object? key = GetKeyFromHandler(subscriber); foreach (Type interfaceType in GetHandlerInterfaces(handlerType)) { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{argumentType}"; if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) { for (int i = subscribers.Count - 1; i >= 0; i--) { if (!subscribers[i].IsAlive || subscribers[i].Target == subscriber) { subscribers.RemoveAt(i); } } } } } } private void RemoveSubscriber(object subscriber, Type argumentType) { string subscriptionKey = $"{argumentType}"; if (subscriptions.TryGetValue(subscriptionKey, out List? subscribers)) { for (int i = subscribers.Count - 1; i >= 0; i--) { if (subscribers[i].Target == subscriber) { subscribers.RemoveAt(i); } } if (subscribers.Count == 0) { subscriptions.TryRemove(subscriptionKey, out _); } } } private object? GetKeyFromHandler(object handler) => handler.GetAttribute() is NotificationAttribute attribute ? handler.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key : null; private IEnumerable GetHandlerInterfaces(Type handlerType) => handlerType.GetInterfaces().Where(interfaceType => { Type? definition = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : null; return definition == typeof(INotificationHandler<>) || definition == typeof(IHandler<>) || definition == typeof(IHandler<,>); }); }