95 lines
3.0 KiB
C#
95 lines
3.0 KiB
C#
using System.Reflection;
|
|
using static System.Reflection.BindingFlags;
|
|
|
|
namespace InnovEnergy.Lib.Utils;
|
|
|
|
public static class Operators
|
|
{
|
|
public static Func<T, T, T> CreateBinaryOpForProps<T>(this String op)
|
|
{
|
|
var methodName = GetMethodNameForBinaryOp(op);
|
|
|
|
var props = typeof(T)
|
|
.GetProperties(Instance | Public)
|
|
.Where(p => p.CanWrite)
|
|
.Select(p =>
|
|
(
|
|
prop: p,
|
|
op: GetOpMethod(methodName, p.PropertyType) ??
|
|
throw new ArgumentException($"Type {p.PropertyType.Name} " +
|
|
$"of property {p.Name} " +
|
|
$"has no suitable {op} operator defined."))
|
|
)
|
|
.ToArray();
|
|
|
|
var ctr = typeof(T).GetConstructors().FirstOrDefault(c => c.GetParameters().Length == 0);
|
|
|
|
if (ctr is null)
|
|
throw new ArgumentException($"Type {typeof(T).Name} has no suitable parameterless constructor.");
|
|
|
|
|
|
T Op(T left, T right)
|
|
{
|
|
// TODO: make this faster/nicer using Expression API (low prio)
|
|
|
|
var result = ctr.Invoke(null);
|
|
|
|
foreach (var (p, m) in props)
|
|
{
|
|
var l = p.GetValue(left);
|
|
var r = p.GetValue(right);
|
|
|
|
var s = m.Invoke(null, new[] { l, r });
|
|
|
|
p.SetValue(result, s);
|
|
}
|
|
|
|
return (T) result;
|
|
}
|
|
|
|
return Op;
|
|
}
|
|
|
|
private static String GetMethodNameForBinaryOp(String op)
|
|
{
|
|
// from https://stackoverflow.com/a/29495075
|
|
|
|
return op switch
|
|
{
|
|
"&" => "op_BitwiseAnd",
|
|
"|" => "op_BitwiseOr",
|
|
"+" => "op_Addition",
|
|
"-" => "op_Subtraction",
|
|
"/" => "op_Division",
|
|
"%" => "op_Modulus",
|
|
"*" => "op_Multiply",
|
|
"<<" => "op_LeftShift",
|
|
">>" => "op_RightShift",
|
|
"^" => "op_ExclusiveOr",
|
|
"==" => "op_Equality",
|
|
"!=" => "op_Inequality",
|
|
">" => "op_GreaterThan",
|
|
"<" => "op_LessThan",
|
|
">=" => "op_GreaterThanOrEqual",
|
|
"<=" => "op_LessThanOrEqual",
|
|
_ => throw new ArgumentException("unknown operator", nameof(op))
|
|
};
|
|
}
|
|
|
|
private static MethodInfo? GetOpMethod(String? methodName, Type type)
|
|
{
|
|
return type
|
|
.GetMethods(Static | Public)
|
|
.FirstOrDefault(m => m.Name == methodName
|
|
&& m.ReturnType == type
|
|
&& m.IsMonoidOp());
|
|
}
|
|
|
|
private static Boolean IsMonoidOp(this MethodInfo m)
|
|
{
|
|
var ps = m.GetParameters();
|
|
|
|
return ps.Length == 2 &&
|
|
ps.All(p => p.ParameterType == m.ReturnType);
|
|
}
|
|
} |