mirror of https://github.com/dahall/Vanara.git
88 lines
4.2 KiB
C#
88 lines
4.2 KiB
C#
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
namespace Vanara.PInvoke;
|
|
|
|
/// <summary>Gets a static field's name from its value and caches the list for faster lookups.</summary>
|
|
public static class StaticFieldValueHash
|
|
{
|
|
private static readonly object cacheLock = new();
|
|
private static readonly Dictionary<(Type, Type), IDictionary<int, (string, string?)>> cache = [];
|
|
|
|
/// <summary>Adds the seqence of field values to the associated cache.</summary>
|
|
/// <typeparam name="TType">The type of the type.</typeparam>
|
|
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
|
/// <param name="fields">The list of field values and names to add.</param>
|
|
/// <param name="lib">The optional library name.</param>
|
|
public static void AddFields<TType, TFieldType>(IEnumerable<(TFieldType value, string name)> fields, string? lib = null)
|
|
where TFieldType : struct, IComparable, IConvertible
|
|
{
|
|
TryGetFieldName<TType, TFieldType>(default, out _); // Load default values
|
|
var tt = (typeof(TType), typeof(TFieldType));
|
|
if (cache.TryGetValue(tt, out var hash))
|
|
{
|
|
foreach (var (value, name) in fields)
|
|
if (!hash.ContainsKey(value.GetHashCode()))
|
|
hash.Add(value.GetHashCode(), (name, lib));
|
|
}
|
|
}
|
|
|
|
/// <summary>Adds the seqence of field values to the associated cache.</summary>
|
|
/// <typeparam name="TType">The type of the type.</typeparam>
|
|
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
|
/// <typeparam name="TEnum">The type of the enum to added.</typeparam>
|
|
/// <param name="lib">The optional library name.</param>
|
|
public static void AddFields<TType, TFieldType, TEnum>(string? lib = null) where TFieldType : struct, IComparable, IConvertible =>
|
|
AddFields<TType, TFieldType>(Enum.GetValues(typeof(TEnum)).Cast<TFieldType>().Select(v => (v, Enum.GetName(typeof(TEnum), v)!)), lib);
|
|
|
|
/// <summary>Tries to get the name of a value's library.</summary>
|
|
/// <typeparam name="TType">The type of the type.</typeparam>
|
|
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
|
/// <param name="value">The value for which to search.</param>
|
|
/// <returns>The name of the library or <see langword="null"/>.</returns>
|
|
public static string? GetFieldLib<TType, TFieldType>(TFieldType value)
|
|
where TFieldType : struct, IComparable, IConvertible
|
|
{
|
|
var tt = (typeof(TType), typeof(TFieldType));
|
|
return cache.TryGetValue(tt, out var hash) && hash.TryGetValue(value.GetHashCode(), out var t) ? t.Item2 : null;
|
|
}
|
|
|
|
/// <summary>Tries to get the name of a static field from it's value.</summary>
|
|
/// <typeparam name="TType">The type of the type.</typeparam>
|
|
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
|
/// <param name="value">The value for which to search.</param>
|
|
/// <param name="fieldName">On success, the name of the field.</param>
|
|
/// <returns><see langword="true"/> if the value was found, otherwise <see langword="false"/>.</returns>
|
|
public static bool TryGetFieldName<TType, TFieldType>(TFieldType value, [NotNullWhen(true)] out string? fieldName)
|
|
where TFieldType : struct, IComparable, IConvertible
|
|
{
|
|
var tt = (typeof(TType), typeof(TFieldType));
|
|
if (!cache.TryGetValue(tt, out var hash))
|
|
{
|
|
hash = typeof(TType).GetFields(BindingFlags.Public | BindingFlags.Static).
|
|
Where(fi => fi.FieldType == typeof(TFieldType)).Distinct(FIValueComp<TFieldType>.Default).
|
|
ToDictionary<FieldInfo, int, (string, string?)>(fi => fi.GetValue(null)!.GetHashCode(), fi => (fi.Name, null));
|
|
lock (cacheLock)
|
|
#if NET5_0_OR_GREATER || NETCOREAPP
|
|
cache.TryAdd(tt, hash);
|
|
#else
|
|
{ if (!cache.ContainsKey(tt)) cache.Add(tt, hash); }
|
|
#endif
|
|
}
|
|
var ret = hash.TryGetValue(value.GetHashCode(), out var t);
|
|
fieldName = t.Item1;
|
|
return ret;
|
|
}
|
|
|
|
private class FIValueComp<TFieldType> : IEqualityComparer<FieldInfo> where TFieldType : struct, IComparable, IConvertible
|
|
{
|
|
bool IEqualityComparer<FieldInfo>.Equals(FieldInfo? x, FieldInfo? y) =>
|
|
Comparer<TFieldType?>.Default.Compare((TFieldType?)x?.GetValue(null), (TFieldType?)y?.GetValue(null)) == 0;
|
|
|
|
int IEqualityComparer<FieldInfo>.GetHashCode(FieldInfo obj) => ((TFieldType?)obj.GetValue(null))?.GetHashCode() ?? 0;
|
|
|
|
public static readonly FIValueComp<TFieldType> Default = new();
|
|
}
|
|
} |