Reputation: 27955
Looking for a way to find is object between two objects. Tried to convert integer to decimal using
static bool Between(object value, object low, object high)
{
if (value == null && low == null && high == null)
return false;
if (value is int || low is int || high is int ||
value is decimal || low is decimal || high is decimal ||
value is double || low is double || high is double ||
value is int? || low is int? || high is int? ||
value is decimal? || low is decimal? || high is decimal? ||
value is double? || low is double? || high is double? )
return (decimal)(value ?? 0) >= (decimal)(low ?? 0) && (decimal)(value ?? 0) <= (decimal)(high ?? 0);
if (value is DateOnly || low is DateOnly || high is DateOnly ||
value is DateOnly? || low is DateOnly? || high is DateOnly?)
return (DateOnly)(value ?? DateOnly.MinValue) >= (DateOnly)(low ?? DateOnly.MinValue) && (DateOnly)(value ?? 0) <= (DateOnly)(high ?? DateOnly.MinValue);
if (value is string || low is string || high is string ||
value is char || low is char || high is char
value is string || low is string || high is string ||
value is char? || low is char? || high is char? )
{
string av = (string)(value ?? "");
string slow = (string)(low ?? "");
string shigh = (string)(high ?? "");
return av.CompareTo(slow) >= 0 && av.CompareTo(shigh) <= 0;
}
throw new ArgumentException("Incompatible argument types");
}
Using mixed int and decimal arguments
Between((int)1 , (decimal)1, (decimal) 2 )
throws exception
Cannot cast integer to decimal
How fix this methid it so it works if arguments are int, decimal, double, int?, decimal? or double? in any combinations. Can using dynamic arument tupes insted of object type make it easier?
Using ASP .NET 8 MVC with latest C#
Upvotes: -3
Views: 99
Reputation: 37440
I would suggest to use Convert.ChangeType
method, that does very well mapping between various number types, so it is safe to pick common, most inclusive numeric type, such as decimal
and use it like below:
bool IsBetweenForNumbers(object value, object low, object high, out bool isBetween)
{
try
{
var cValue = (decimal)Convert.ChangeType(value, typeof(decimal));
var cLow = (decimal)Convert.ChangeType(low, typeof(decimal));
var cHigh = (decimal)Convert.ChangeType(high, typeof(decimal));
isBetween = cLow < cValue && cHigh > cValue;
return true;
}
catch
{
isBetween = false;
return false;
}
}
Now it is very flexible, it correctly handles such use cases:
var r1 = IsBetweenForNumbers((int)1, (decimal)1, (decimal)2, out var b1);
var r2 = IsBetweenForNumbers("1", 2, 5M, out var b2);
var r3 = IsBetweenForNumbers(3F, 2M, 5, out var b3);
This method should be incorporated in yours to avoid problems you have.
Reference: Convert.ChangeType
Upvotes: 2
Reputation: 56
How about this approach?
static class Utility
{
static bool IsNumericType(object value) =>
value is int || value is decimal || value is double ||
value is int? || value is decimal? || value is double?;
static bool IsDateOnlyType(object value) =>
value is DateOnly || value is DateOnly?;
static bool IsStringOrCharType(object value) =>
value is string || value is char || value is char?;
static decimal ToDecimal(object value) =>
decimal.TryParse(value.ToString(), out var result) ? result : 0;
static DateOnly ToDateOnly(object value) => (DateOnly)(value ?? DateOnly.MinValue);
static string ToStringValue(object value) => (string)(value ?? "");
static bool CompareNumeric(object value, object low, object high) =>
ToDecimal(value) >= ToDecimal(low) && ToDecimal(value) <= ToDecimal(high);
static bool CompareDateOnly(object value, object low, object high) =>
ToDateOnly(value) >= ToDateOnly(low) && ToDateOnly(value) <= ToDateOnly(high);
static bool CompareStringOrChar(object value, object low, object high) =>
ToStringValue(value).CompareTo(ToStringValue(low)) >= 0 &&
ToStringValue(value).CompareTo(ToStringValue(high)) <= 0;
public static bool Between(object value, object low, object high)
{
if (value == null && low == null && high == null)
return false;
if (IsNumericType(value) || IsNumericType(low) || IsNumericType(high))
return CompareNumeric(value, low, high);
if (IsDateOnlyType(value) || IsDateOnlyType(low) || IsDateOnlyType(high))
return CompareDateOnly(value, low, high);
if (IsStringOrCharType(value) || IsStringOrCharType(low) || IsStringOrCharType(high))
return CompareStringOrChar(value, low, high);
throw new ArgumentException("Incompatible argument types");
}
}
Or the following approach that preserves object-oriented encapsulation?
public class Value
{
public object Data { get; private set; }
public Value(object data)
{
Data = data;
}
public bool IsNumeric() =>
Data is int || Data is decimal || Data is double ||
Data is int? || Data is decimal? || Data is double?;
public bool IsDateOnly() =>
Data is DateOnly || Data is DateOnly?;
public bool IsStringOrChar() =>
Data is string || Data is char || Data is char?;
public decimal ToDecimal() =>
decimal.TryParse(Data.ToString(), out var result) ? result : 0;
public DateOnly ToDateOnly() =>
Data is DateOnly date ? date : DateOnly.MinValue;
public string ToStringValue() =>
Data?.ToString() ?? string.Empty;
}
public static class Utility
{
public static bool Between(Value value, Value low, Value high)
{
if (value == null && low == null && high == null)
return false;
if (value.IsNumeric() || low.IsNumeric() || high.IsNumeric())
return value.ToDecimal() >= low.ToDecimal() && value.ToDecimal() <= high.ToDecimal();
if (value.IsDateOnly() || low.IsDateOnly() || high.IsDateOnly())
return value.ToDateOnly() >= low.ToDateOnly() && value.ToDateOnly() <= high.ToDateOnly();
if (value.IsStringOrChar() || low.IsStringOrChar() || high.IsStringOrChar())
return value.ToStringValue().CompareTo(low.ToStringValue()) >= 0 &&
value.ToStringValue().CompareTo(high.ToStringValue()) <= 0;
throw new ArgumentException("Incompatible argument types");
}
}
// Usage
Value intValue = new Value((int)5);
Value lowInt = new Value((decimal)1);
Value highInt = new Value((decimal)10);
bool isInRangeInt = Utility.Between(intValue, lowInt, highInt);
Upvotes: 0
Reputation: 56
As a quick solution, you can use decimal.TryParse() and ToString().
object input1 = (int)1;
object input2 = (int)2;
decimal.TryParse(input1.ToString(), out decimal parse);
bool compareBool1 = (decimal)parse <= (decimal)input2; // This works.
bool compareBool2 = (decimal)input1 <= (decimal)input2; // InvalidCastException
However, there are still many issues.
Upvotes: 0