using System.Collections.Generic;
using System.Linq.Expressions;
using System.Diagnostics.CodeAnalysis;
using AgileObjects.ReadableExpressions;
public int Val { get; set; }
public Foo(int val) => Val = val;
public interface IValueModifier
where T : IComparable<T>, IEquatable<T>;
where T : IComparable<T>, IEquatable<T>;
public interface IValueModifier<T> : IValueModifier
where T : IComparable<T>, IEquatable<T>
public abstract class ValueModifier<T> : IValueModifier<T>
where T : IComparable<T>, IEquatable<T>
T2 IValueModifier.Incr<T2>(T2 val)
var asT = Incr((T)(object)val);
public abstract T Incr(T val);
T2 IValueModifier.Decr<T2>(T2 val)
var asT = Decr((T)(object)val);
public abstract T Decr(T val);
public class IntModifier : ValueModifier<int>
public override int Incr(int val)
return val <= (int.MaxValue - 1) ?
throw new ArgumentOutOfRangeException(nameof(val));
public override int Decr(int val)
return val >= (int.MinValue + 1) ?
throw new ArgumentOutOfRangeException(nameof(val));
public static class ValueModifierFactory<T>
where T : IComparable<T>, IEquatable<T>
public static IValueModifier<T> Create()
Type t when t == typeof(int) => (IValueModifier<T>)new IntModifier(),
_ => throw new InvalidOperationException(
$"Invalid type {typeof(T).Name} for {typeof(IValueModifier<T>).Name}."
public class Mutater<TObj>
private static Dictionary<Type, IValueModifier> modifiers = new();
public TObj Val { get; init; }
public Mutater(TObj val) => Val = val;
private void MutateProperty<TProp>(ref TProp prop, Expression<Func<TProp, bool>> tester)
where TProp : IComparable<TProp>, IEquatable<TProp>
if (!modifiers.ContainsKey(typeof(TProp)))
modifiers[typeof(TProp)] = ValueModifierFactory<TProp>.Create();
IValueModifier<TProp> modifier = (IValueModifier<TProp>)modifiers[typeof(TProp)];
Console.WriteLine($"Compiling tester");
var testerCompiled = tester.Compile();
Console.WriteLine($"Getting result");
var result = testerCompiled(prop);
Console.WriteLine($"Result was true. Modifying prop until it is false.");
tmp = modifier.Decr(prop);
Console.WriteLine($"prop is now {prop}");
result = testerCompiled(tmp);
Console.WriteLine($"Result was false. Modifying prop until it is true.");
prop = modifier.Incr(prop);
Console.WriteLine($"prop is now {prop}");
result = testerCompiled(prop);
Console.WriteLine($"Broken out. prop is {prop}");
public TProp? GetFirstPassingValue<TProp>(Expression<Func<TObj, TProp>> getterExpr, Expression<Func<TProp, bool>> testerExpr)
where TProp : IComparable<TProp>, IEquatable<TProp>
var getter = getterExpr.Compile();
var tester = testerExpr.Compile();
MutateProperty(ref prop, testerExpr);
public static void Main()
var mutater = new Mutater<Foo>(foo);
var firstPassing = mutater.GetFirstPassingValue(foo => foo.Val, val => val > -5);
Console.WriteLine($"firstPassing: {firstPassing}");