← All source files

V18 / 8d896b8a86e5

ReflectionApi.cs

Interfaces/Gui/LilithGui.Worker/ReflectionApi.cs

Unchanged from the preceding version
CS
using System.Reflection;
using System.Text.Json;

namespace Lilith.Gui.Worker;

internal static class ReflectionApi
{
    internal static object? Get(object? value,string property)=>value?.GetType().GetProperty(property,BindingFlags.Public|BindingFlags.Instance)?.GetValue(value);
    internal static object Create(Type type,IReadOnlyDictionary<string,object?> values)
    {
        var constructor=type.GetConstructors().OrderByDescending(c=>c.GetParameters().Length).First();
        return constructor.Invoke(Arguments(constructor.GetParameters(),values));
    }
    private static object?[] Arguments(ParameterInfo[] parameters,IReadOnlyDictionary<string,object?> values)=>parameters.Select(p=>
    {
        var match=values.FirstOrDefault(v=>string.Equals(v.Key,p.Name,StringComparison.OrdinalIgnoreCase));
        if(match.Key is not null)return ConvertValue(match.Value,p.ParameterType);
        return p.HasDefaultValue?p.DefaultValue:p.ParameterType==typeof(CancellationToken)?CancellationToken.None:throw new ArgumentException("Missing "+p.Name);
    }).ToArray();
    private static object? ConvertValue(object? value,Type type)
    {
        if(value is null||type.IsInstanceOfType(value))return value;
        if(type.IsEnum)return Enum.Parse(type,value.ToString()!,true);
        if(value is JsonElement json)return JsonSerializer.Deserialize(json.GetRawText(),type,new JsonSerializerOptions{PropertyNameCaseInsensitive=true});
        return Convert.ChangeType(value,Nullable.GetUnderlyingType(type)??type,System.Globalization.CultureInfo.InvariantCulture);
    }
    internal static async Task<object?> Call(object target,string method,IReadOnlyDictionary<string,object?>? values=null)
    {
        var candidates=target.GetType().GetMethods(BindingFlags.Public|BindingFlags.Instance).Where(m=>m.Name==method).OrderByDescending(m=>m.GetParameters().Length).ToArray();
        if(candidates.Length==0)throw new NotSupportedException("This release does not expose "+method+".");
        object? result=candidates[0].Invoke(target,Arguments(candidates[0].GetParameters(),values??new Dictionary<string,object?>()));
        if(result is Task task){await task;return task.GetType().GetProperty("Result")?.GetValue(task);}
        return result;
    }
    internal static void Attach(object target,string name,Action<object> callback)
    {
        var info=target.GetType().GetEvent(name);if(info?.EventHandlerType is null)return;
        info.AddEventHandler(target,new EventForwarder(callback).Create(info.EventHandlerType));
    }
    internal sealed class EventForwarder(Action<object> callback)
    {
        public void Forward<T>(T value)=>callback(value!);
        internal Delegate Create(Type delegateType)=>GetType().GetMethod(nameof(Forward))!.MakeGenericMethod(delegateType.GenericTypeArguments[0]).CreateDelegate(delegateType,this);
    }
}