Reputation: 13199
Sorry that I had to re-edit this question.
I need to dynamically parse two string values to the appropriate type and compare them, then return a bool result.
Example 1:
string lhs = “10”;
string rhs = “10”;
Compare.DoesEqual(lhs, rhs, typeof(int)); //true
Compare.DoesEqual(lhs, rhs, typeof(string)); //true
Example 2:
string lhs = “2.0”;
string rhs = “3.1”;
Compare.IsGreaterThan(lhs, rhs, typeof(int)); //false
Compare.IsGreaterThan(lhs, rhs, typeof(double)); //false
Compare.IsGreaterThan(lhs, rhs, typeof(string)); //invalid, always false
Currently I am doing like this (I think it's stupid to do it such way):
public partial class Comparer
{
public static bool DoesEqual(string lhs, string rhs, Type type)
{
if (type.Equals(typeof(int)))
{
try
{
return int.Parse(lhs) > int.Parse(rhs);
}
catch
{
return false;
}
}
if (type.Equals(typeof(double)))
{
try
{
return double.Parse(lhs) > double.Parse(rhs);
}
catch
{
return false;
}
}
return false;
}
}
And this:
public partial class Comparer
{
public static bool IsGreaterThan(string lhs, string rhs, Type type)
{
if (type.Equals(typeof(int)))
{
try
{
return int.Parse(lhs) == int.Parse(rhs);
}
catch
{
return false;
}
}
if (type.Equals(typeof(double)))
{
try
{
return double.Parse(lhs) == double.Parse(rhs);
}
catch
{
return false;
}
}
if (type.Equals(typeof(string)))
{
return lhs.Equals(rhs);
}
return false;
}
}
I am looking for better a better (more generic way) implementation (perhaps using Expression Tree?). I appreciate any suggestions. Thank you!
Upvotes: 1
Views: 4061
Reputation: 99889
Oh I am so about to own this problem. :)
Edit: The tests:
[TestMethod]
public void TestDynamicComparer()
{
string lhs = "10";
string rhs = "10";
Assert.AreEqual(0, DynamicComparer<int>.Default.Compare(lhs, rhs));
lhs = "2.0";
rhs = "3.1";
// basic equality
Assert.AreNotEqual(0, DynamicComparer<double>.Default.Compare(lhs, rhs));
// correct order
Assert.IsTrue(DynamicComparer<double>.Default.Compare(lhs, rhs) < 0);
// check two invalid casts are unordered
Assert.AreEqual(0, DynamicComparer<int>.DefaultNoThrow.Compare(lhs, rhs));
// real proof it works
lhs = "9";
rhs = "09";
Assert.AreEqual(0, DynamicComparer<int>.Default.Compare(lhs, rhs));
lhs = "9.0";
rhs = "09";
Assert.AreEqual(0, DynamicComparer<double>.Default.Compare(lhs, rhs));
// test the valid cast is ordered ahead of ("less than") the invalid cast
Assert.AreNotEqual(0, DynamicComparer<int>.DefaultNoThrow.Compare(lhs, rhs));
Assert.IsTrue(DynamicComparer<int>.DefaultNoThrow.Compare(lhs, rhs) > 0);
}
[TestMethod]
[ExpectedException(typeof(InvalidCastException))]
public void TestDynamicComparerInvalidCast()
{
// make sure the default comparer throws an InvalidCastException if a cast fails
string lhs = "2.0";
string rhs = "3.1";
DynamicComparer<int>.Default.Compare(lhs, rhs);
}
The guts:
public class DynamicComparer<T>
: IComparer<string>
, IEqualityComparer<string>
{
private static readonly DynamicComparer<T> defaultComparer = new DynamicComparer<T>();
private static readonly DynamicComparer<T> defaultNoThrowComparer = new DynamicComparer<T>(false);
private DynamicComparerHelper.TryParseDelegate<T> parser = DynamicComparerHelper.GetParser<T>();
private IComparer<T> comparer;
private bool throwOnError;
public DynamicComparer()
: this(Comparer<T>.Default, true)
{
}
public DynamicComparer(bool throwOnError)
: this(Comparer<T>.Default, throwOnError)
{
}
public DynamicComparer(IComparer<T> comparer)
: this(comparer, true)
{
}
public DynamicComparer(IComparer<T> comparer, bool throwOnError)
{
this.comparer = comparer;
this.throwOnError = throwOnError;
}
public static DynamicComparer<T> Default
{
get
{
return defaultComparer;
}
}
public static DynamicComparer<T> DefaultNoThrow
{
get
{
return defaultNoThrowComparer;
}
}
public int Compare(string x, string y)
{
T valueX;
T valueY;
bool convertedX = this.parser(x, out valueX);
bool convertedY = this.parser(y, out valueY);
if (this.throwOnError && !(convertedX && convertedY))
throw new InvalidCastException();
if (!(convertedX || convertedY))
return 0;
if (!convertedX)
return 1;
if (!convertedY)
return -1;
return this.comparer.Compare(valueX, valueY);
}
public bool Equals(string x, string y)
{
return Compare(x, y) == 0;
}
public int GetHashCode(string x)
{
T value;
bool converted = this.parser(x, out value);
if (this.throwOnError && !converted)
throw new InvalidCastException();
if (!converted)
return 0;
return value.GetHashCode();
}
}
internal class DynamicComparerHelper
{
public delegate bool TryParseDelegate<T>(string text, out T value);
private static readonly Dictionary<Type, Delegate> converters =
new Dictionary<Type, Delegate>()
{
{ typeof(bool), WrapDelegate<bool>(bool.TryParse) },
{ typeof(short), WrapDelegate<short>(short.TryParse) },
{ typeof(int), WrapDelegate<int>(int.TryParse) },
{ typeof(long), WrapDelegate<long>(long.TryParse) },
{ typeof(ushort), WrapDelegate<ushort>(ushort.TryParse) },
{ typeof(uint), WrapDelegate<uint>(uint.TryParse) },
{ typeof(ulong), WrapDelegate<ulong>(ulong.TryParse) },
{ typeof(float), WrapDelegate<float>(float.TryParse) },
{ typeof(double), WrapDelegate<double>(double.TryParse) },
{ typeof(DateTime), WrapDelegate<DateTime>(DateTime.TryParse) },
};
public static TryParseDelegate<T> GetParser<T>()
{
return (TryParseDelegate<T>)converters[typeof(T)];
}
private static TryParseDelegate<T> WrapDelegate<T>(TryParseDelegate<T> parser)
{
return new TryParseDelegate<T>(parser);
}
}
Upvotes: 2
Reputation: 101595
The most generic way is to use TypeConverter
. Let's say we have:
string rhs = ...;
string lhs = ...;
Type type = ...;
from somewhere. You can do:
TypeConverter conv = TypeDescriptor.GetConverter(type);
try
{
object rho = conv.ConvertFrom(rhs);
object lho = conv.ConvertFrom(lhs);
...
}
catch (NotSupportedException)
{
// No luck - couldn't parse the string
}
Unfortunately, there's no way to avoid a catch
there when dealing with converters. Now that you have two objects parsed from strings, you can do generic comparisons. Equality is simple - just use Object.Equals
(preferably static one - it handles nulls):
if (Object.Equals(rho, lho))
{
...
}
For ordering comparisons, you can check if the type supports IComparable
:
IComparable rhc = rho as IComparable;
if (rhc != null && rhc.CompareTo(lho) < 0) // rhs less than lhs
{
...
}
Note however that some types that don't provide operator<
are still ordered, and thus implement IComparable
- for example, String
does that.
Upvotes: 3
Reputation: 55092
Is this homework?
I mean, the answer is to just write the implementation for the class/method.
What is the specific problem you are having? What is it you don't understand, exactly?
If you break the problem down into parts it becomes easy.
Upvotes: -2