This action has a name in signal processing: clipping.
And here there is the totally useless DecimalRestrictor<T>
, based on a dinamically built Expression Tree in the form
x => x <= min ? min : x >= max ? max : (T)x;
plus an exception for decimal
, float
and double
: all the three types can accept any decimal
value.
The code:
public static class DecimalClipper<T>
{
public static readonly Func<decimal, T> Clip;
static DecimalClipper()
{
ParameterExpression value = Expression.Parameter(typeof(decimal), "value");
Expression<Func<decimal, T>> lambda;
if (typeof(T) == typeof(decimal))
{
lambda = Expression.Lambda<Func<decimal, T>>(value, value);
}
else if (typeof(T) == typeof(float) || typeof(T) == typeof(double))
{
lambda = Expression.Lambda<Func<decimal, T>>(Expression.Convert(value, typeof(T)), value);
}
else
{
T min = (T)typeof(T).GetField("MinValue", BindingFlags.Static | BindingFlags.Public).GetValue(null);
Expression minT = Expression.Constant(min);
Expression minDecimal = Expression.Constant(Convert.ToDecimal(min));
T max = (T)typeof(T).GetField("MaxValue", BindingFlags.Static | BindingFlags.Public).GetValue(null);
Expression maxT = Expression.Constant(max);
Expression maxDecimal = Expression.Constant(Convert.ToDecimal(max));
UnaryExpression cast = Expression.Convert(value, typeof(T));
ConditionalExpression greaterThanOrEqual = Expression.Condition(Expression.GreaterThanOrEqual(value, maxDecimal), maxT, cast);
ConditionalExpression lesserThanOrEqual = Expression.Condition(Expression.LessThanOrEqual(value, minDecimal), minT, greaterThanOrEqual);
lambda = Expression.Lambda<Func<decimal, T>>(lesserThanOrEqual, value);
}
Clip = lambda.Compile();
}
}
public static class DecimalEx
{
public static T Clip<T>(this decimal value)
{
return DecimalClipper<T>.Clip(value);
}
}
I'm even including an extension method...
Examples of use:
int i1 = decimal.MaxValue.Clip<int>();
int i2 = decimal.MinValue.Clip<int>();
int i3 = 5.5M.Clip<int>();
int i4 = -5.5M.Clip<int>();
byte i5 =(-5.5M).Clip<byte>();
//char i6 = decimal.MaxValue.Clip<char>();
float i7 = decimal.MaxValue.Clip<float>();
double i8 = decimal.MaxValue.Clip<double>();
decimal i9 = decimal.MaxValue.Clip<decimal>();
Ah... the expression tree is generated only once for each type T
used and then cached thanks to the working of generic types and static members.
The char
, IntPtr
, UIntPtr
aren't supported at this time.