com.alicizax.unity.framework/Runtime/ABase/Service/Core/ServiceContractUtility.cs

79 lines
3.1 KiB
C#

using System;
using System.Collections.Generic;
namespace AlicizaX
{
internal static class ServiceContractUtility
{
private static readonly HashSet<Type> ExcludedContracts = new HashSet<Type>
{
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<Type, List<Type>> _contractCache = new Dictionary<Type, List<Type>>();
public static List<Type> Collect(Type serviceType, IReadOnlyList<Type> 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<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)
{
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.");
}
}
}