using System.Linq.Expressions; using System.Numerics; using System.Reflection; namespace AIStudio.Tools; public static class ExpressionExtensions { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(typeof(ExpressionExtensions)); /// /// Extracts the member expression from a given lambda expression representing a property. /// /// A lambda expression specifying the property for which the member expression is to be extracted. /// The lambda expression body must represent member access. /// The type of the object containing the property referenced in the lambda expression. /// The type of the property being accessed in the lambda expression. /// The member expression that represents the property access. /// Thrown if the provided lambda expression does not represent a valid property expression. public static MemberExpression GetMemberExpression(this Expression> expression) { switch (expression.Body) { // Case for value types, which are wrapped in UnaryExpression: case UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression: return (MemberExpression)unaryExpression.Operand; // Case for reference types, which are directly MemberExpressions: case MemberExpression memberExpression: return memberExpression; default: LOGGER.LogError($"Expression '{expression}' is not a valid property expression."); throw new ArgumentException($"Expression '{expression}' is not a valid property expression.", nameof(expression)); } } /// /// Attempts to increment the value of an uint property for a specified object using a /// provided expression. /// /// An expression representing the property to be incremented. The property /// must be of type uint and belong to the provided object. /// The object that contains the property referenced by the expression. /// The type of increment operation to perform (e.g., prefix or postfix). /// The type of the object that contains the property to be incremented. /// The type of the property to be incremented. /// An IncrementResult object containing the result of the increment operation. public static IncrementResult TryIncrement(this Expression> expression, TIn data, IncrementType type) where TOut : IBinaryInteger { // Ensure that the expression body is a member expression: if (expression.Body is not MemberExpression memberExpression) return new(false, TOut.Zero); // Ensure that the member expression is a property: if (memberExpression.Member is not PropertyInfo propertyInfo) return new(false, TOut.Zero); // Ensure that the member expression has a target object: if (memberExpression.Expression is null) return new(false, TOut.Zero); // Get the target object for the expression, which is the object containing the property to increment: var targetObjectExpression = Expression.Lambda(memberExpression.Expression, expression.Parameters); // Compile the lambda expression to get the target object // (which is the object containing the property to increment): var targetObject = targetObjectExpression.Compile().DynamicInvoke(data); // Was the compilation successful? if (targetObject is null) return new(false, TOut.Zero); // Read the current value of the property: if (propertyInfo.GetValue(targetObject) is not TOut value) return new(false, TOut.Zero); // Increment the value: switch (type) { case IncrementType.PRE: var nextValue = value + TOut.CreateChecked(1); propertyInfo.SetValue(targetObject, nextValue); return new(true, nextValue); case IncrementType.POST: var currentValue = value; var incrementedValue = value + TOut.CreateChecked(1); propertyInfo.SetValue(targetObject, incrementedValue); return new(true, currentValue); default: return new(false, TOut.Zero); } } }