Yes, it is possible to do this in one line with no type checking. There is one caveat though - this method will not play nice if the concrete type doesn't implement IComparable
or IComparable<T>
. If you don't implement one of those interfaces then you will get a System.ArgumentException, but this is easily accommodated (especially as it is most likely to occur for one of your own custom types, I don't think there are many BCL types that don't implement it).
All you need is this nifty line of code:
bool isNegative = Comparer<T>.Default.Compare(value, default(T)) < 0;
The real beauty in this one is the default(T)
- this is what accommodates strings, DateTimes, etc. The concrete type will get compared to the default value of the type, and for the likes of string or DateTime the Compare()
will never return less than zero when comparing to the default.
Check out this sample, which includes an example custom struct so you can see the IComparable
in action:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Comparing different value (and reference) types to zero:");
Console.WriteLine();
Console.WriteLine("Checking int '9' : {0}", IsNegative(9));
Console.WriteLine("Checking int '-9': {0}", IsNegative(-9));
Console.WriteLine("Checking int '0' : {0}", IsNegative(0));
Console.WriteLine();
Console.WriteLine("Checking decimal '9' : {0}", IsNegative(9m));
Console.WriteLine("Checking decimal '-9': {0}", IsNegative(-9m));
Console.WriteLine("Checking decimal '0' : {0}", IsNegative(0m));
Console.WriteLine();
Console.WriteLine("Checking float '9' : {0}", IsNegative(9f));
Console.WriteLine("Checking float '-9' : {0}", IsNegative(-9f));
Console.WriteLine("Checking float '0' : {0}", IsNegative(0f));
Console.WriteLine();
Console.WriteLine("Checking long '9' : {0}", IsNegative(9L));
Console.WriteLine("Checking long '-9' : {0}", IsNegative(-9L));
Console.WriteLine("Checking long '0' : {0}", IsNegative(0L));
Console.WriteLine();
Console.WriteLine("Checking string '9' : {0}", IsNegative("9"));
Console.WriteLine("Checking string '-9' : {0}", IsNegative("-9"));
Console.WriteLine("Checking string empty : {0}", IsNegative(string.Empty));
Console.WriteLine("Checking string null : {0}", IsNegative((string)null));
Console.WriteLine();
Console.WriteLine("Checking DateTime.Now '{0}' : {1}", DateTime.Now, IsNegative(DateTime.Now));
Console.WriteLine("Checking DateTime.Max '{0}' : {1}", DateTime.MaxValue, IsNegative(DateTime.MaxValue));
Console.WriteLine("Checking DateTime.Min '{0}' : {1}", DateTime.MinValue, IsNegative(DateTime.MinValue));
Console.WriteLine();
Console.WriteLine("Checking positive MyStruct : {0}", IsNegative(new MyStruct() { MainValue = 9 }));
Console.WriteLine("Checking negative MyStruct : {0}", IsNegative(new MyStruct() { MainValue = -9 }));
Console.WriteLine("Checking zero MyStruct : {0}", IsNegative(new MyStruct() { MainValue = 0 }));
Console.WriteLine();
Console.ReadKey();
}
private static bool IsNegative<T>(T value)
{
return Comparer<T>.Default.Compare(value, default(T)) < 0;
}
private struct MyStruct : IComparable<MyStruct>, IComparer<MyStruct>
{
public int MainValue;
public int CompareTo(MyStruct other)
{
return MainValue - other.MainValue;
}
public int Compare(MyStruct x, MyStruct y)
{
return x.MainValue - y.MainValue;
}
}
}