add CreateBinaryOpForProps
This commit is contained in:
parent
3758165c08
commit
a32cf83893
|
@ -0,0 +1,95 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue