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