2026-03-26 10:49:41 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
namespace AlicizaX
|
|
|
|
|
{
|
|
|
|
|
internal static class ServiceContractUtility
|
|
|
|
|
{
|
|
|
|
|
private static readonly HashSet<Type> ExcludedContracts = new HashSet<Type>
|
|
|
|
|
{
|
|
|
|
|
typeof(IService),
|
2026-04-20 13:46:44 +08:00
|
|
|
typeof(IMonoService),
|
2026-03-26 10:49:41 +08:00
|
|
|
typeof(IServiceTickable),
|
|
|
|
|
typeof(IServiceLateTickable),
|
|
|
|
|
typeof(IServiceFixedTickable),
|
|
|
|
|
typeof(IServiceGizmoDrawable),
|
|
|
|
|
typeof(IServiceOrder),
|
2026-04-20 13:46:44 +08:00
|
|
|
typeof(IServiceLifecycle),
|
2026-03-26 10:49:41 +08:00
|
|
|
};
|
|
|
|
|
|
2026-03-26 13:51:09 +08:00
|
|
|
// Cache for the common no-extraContracts path — contracts per concrete type never change.
|
|
|
|
|
private static readonly Dictionary<Type, List<Type>> _contractCache = new Dictionary<Type, List<Type>>();
|
|
|
|
|
|
2026-03-26 10:49:41 +08:00
|
|
|
public static List<Type> Collect(Type serviceType, IReadOnlyList<Type> extraContracts)
|
2026-03-26 13:51:09 +08:00
|
|
|
{
|
|
|
|
|
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<Type>(contracts); // don't mutate the cached list
|
|
|
|
|
var unique = new HashSet<Type>(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<Type> BuildContracts(Type serviceType)
|
2026-03-26 10:49:41 +08:00
|
|
|
{
|
|
|
|
|
var contracts = new List<Type> { serviceType };
|
|
|
|
|
var unique = new HashSet<Type> { 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.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|