116 lines
3.9 KiB
C#
116 lines
3.9 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using AyCode.Services.SignalRs;
|
|
|
|
namespace AyCode.Models.Server.DynamicMethods;
|
|
|
|
/// <summary>
|
|
/// Registry for dynamic method lookups with lazy initialization.
|
|
/// Caches method metadata statically (by messageTag), resolves instances per-request.
|
|
/// </summary>
|
|
/// <typeparam name="TAttribute">The attribute type used to mark methods (e.g., SignalRAttribute)</typeparam>
|
|
public class AcDynamicMethodRegistry<TAttribute> where TAttribute : TagAttribute
|
|
{
|
|
/// <summary>
|
|
/// Statikus cache: messageTag → (DeclaringType, MethodInfo)
|
|
/// A reflection eredménye, nem változik runtime-ban.
|
|
/// null érték = már kerestük, de nem találtuk.
|
|
/// </summary>
|
|
private static readonly ConcurrentDictionary<int, (Type DeclaringType, AcMethodInfoModel<TAttribute> Method)?> _methodLookupCache = new();
|
|
|
|
/// <summary>
|
|
/// Instance array - NEM statikus, request/Hub-specifikus.
|
|
/// Array is faster than List for small fixed-size collections (2-5 elements).
|
|
/// </summary>
|
|
private object[] _instances = [];
|
|
private int _count;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the capacity of the instance array.
|
|
/// Set this before calling Register() to avoid allocations.
|
|
/// </summary>
|
|
public int CahcheSizeCapacity
|
|
{
|
|
get => _instances.Length;
|
|
set => _instances = new object[value];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers an instance for method lookup.
|
|
/// No reflection happens here - just stores the instance reference.
|
|
/// </summary>
|
|
public void Register(object instance)
|
|
{
|
|
if (_count >= _instances.Length)
|
|
{
|
|
// CahcheSizeCapacity not set or exceeded - resize
|
|
var newSize = _instances.Length == 0 ? 4 : _instances.Length * 2;
|
|
Array.Resize(ref _instances, newSize);
|
|
}
|
|
_instances[_count++] = instance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the method and instance for a given messageTag.
|
|
/// Uses cached lookup when possible, falls back to lazy search.
|
|
/// </summary>
|
|
public (object Instance, AcMethodInfoModel<TAttribute> Method)? GetMethodByMessageTag(int messageTag)
|
|
{
|
|
// 1. Check cache first
|
|
if (_methodLookupCache.TryGetValue(messageTag, out var cached))
|
|
{
|
|
if (cached == null)
|
|
return null; // Already searched, not found
|
|
|
|
// Find the instance of the cached type
|
|
var instance = FindInstanceByType(cached.Value.DeclaringType);
|
|
if (instance != null)
|
|
return (instance, cached.Value.Method);
|
|
}
|
|
|
|
// 2. Lazy search through registered instances
|
|
for (var i = 0; i < _count; i++)
|
|
{
|
|
var instance = _instances[i];
|
|
var type = instance.GetType();
|
|
|
|
// Search methods with TAttribute
|
|
foreach (var methodInfo in type.GetMethods())
|
|
{
|
|
if (methodInfo.GetCustomAttribute(typeof(TAttribute)) is not TAttribute attribute)
|
|
continue;
|
|
|
|
if (attribute.MessageTag == messageTag)
|
|
{
|
|
var method = new AcMethodInfoModel<TAttribute>(attribute, methodInfo);
|
|
_methodLookupCache[messageTag] = (type, method);
|
|
return (instance, method);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Not found - cache this result too
|
|
_methodLookupCache[messageTag] = null;
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds an instance by its type from the registered instances.
|
|
/// </summary>
|
|
private object? FindInstanceByType(Type type)
|
|
{
|
|
for (var i = 0; i < _count; i++)
|
|
{
|
|
var instance = _instances[i];
|
|
if (instance.GetType() == type)
|
|
return instance;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of registered instances.
|
|
/// </summary>
|
|
public int Count => _count;
|
|
}
|