using System; using System.Collections.Generic; namespace AlicizaX { internal static class ServiceContractUtility { private static readonly HashSet ExcludedContracts = new HashSet { typeof(IService), typeof(IMonoService), typeof(IServiceTickable), typeof(IServiceLateTickable), typeof(IServiceFixedTickable), typeof(IServiceGizmoDrawable), typeof(IServiceOrder), typeof(IServiceLifecycle), }; // Cache for the common no-extraContracts path — contracts per concrete type never change. private static readonly Dictionary> _contractCache = new Dictionary>(); public static List Collect(Type serviceType, IReadOnlyList extraContracts) { if (extraContracts == null || extraContracts.Count == 0) { if (_contractCache.TryGetValue(serviceType, out var cached)) return cached; var result = BuildContracts(serviceType); _contractCache[serviceType] = result; return result; } // Extra contracts path: build fresh, validate, append extras. var contracts = BuildContracts(serviceType); contracts = new List(contracts); // don't mutate the cached list var unique = new HashSet(contracts); for (var i = 0; i < extraContracts.Count; i++) { var extraContract = extraContracts[i]; ValidateExtraContract(serviceType, extraContract); if (unique.Add(extraContract)) contracts.Add(extraContract); } return contracts; } private static List BuildContracts(Type serviceType) { var contracts = new List { serviceType }; var unique = new HashSet { serviceType }; var interfaces = serviceType.GetInterfaces(); for (var i = 0; i < interfaces.Length; i++) { var contract = interfaces[i]; if (!typeof(IService).IsAssignableFrom(contract)) continue; if (ExcludedContracts.Contains(contract)) continue; if (unique.Add(contract)) contracts.Add(contract); } return contracts; } private static void ValidateExtraContract(Type serviceType, Type contract) { if (contract == null) throw new ArgumentNullException(nameof(contract)); if (!typeof(IService).IsAssignableFrom(contract)) throw new InvalidOperationException($"{contract.FullName} must inherit {nameof(IService)}."); if (!contract.IsAssignableFrom(serviceType)) throw new InvalidOperationException($"{serviceType.FullName} does not implement {contract.FullName}."); if (ExcludedContracts.Contains(contract)) throw new InvalidOperationException($"{contract.FullName} cannot be used as a service contract."); } } }