using System.Reactive.Disposables; namespace Toolkit.Foundation; public class Subscription(SubscriptionCollection subscriptions, IDisposer disposer) : ISubscription { public void Add(object subscriber) { Type handlerType = subscriber.GetType(); IDictionary keys = GetKeysFromHandler(subscriber); foreach (Type interfaceType in GetHandlerInterfaces(handlerType)) { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { keys.TryGetValue(argumentType, out object? key); 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, subscriptionKey))); } } } public void Remove(object subscriber) { Type handlerType = subscriber.GetType(); IDictionary keys = GetKeysFromHandler(subscriber); foreach (Type interfaceType in GetHandlerInterfaces(handlerType)) { if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType) { keys.TryGetValue(argumentType, out object? key); 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, string key) { if (subscriptions.TryGetValue(key, 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(key, out _); } } } //private object? GetKeyFromHandler(object handler) => // handler.GetAttribute() is NotificationAttribute attribute // ? handler.GetPropertyValue(() => attribute.Key) is { } value ? value : attribute.Key : null; private IDictionary GetKeysFromHandler(object handler) { Dictionary keys = []; foreach (NotificationAttribute attribute in handler.GetAttributes()) { keys.Add(attribute.Type, attribute.Key); } return keys; } 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<,>); }); }