Cannot pass a default comparison parameter like IComparer <object>
I am trying to call a Sort method that expects a type parameter IComparer<object>
using the code:
collection.Sort((IComparer<object>)Comparer<DateTime>.Default)
It builds, but at runtime I get an InvalidCastException with the message:
Unable to cast object of type
'System.Collections.Generic.GenericComparer`1[System.DateTime]'
to type 'System.Collections.Generic.IComparer`1[System.Object]'.
Now what?
If you want to compare by default, this will work:
collection.Sort(Comparer<object>.Default)
Comparer.Default uses built-in object comparison semantics (i.e. IComparable.CompareTo).
Unfortunately, you must have a matcher of the appropriate type.
You can create your own IComparer<object>
class that just completes the DateTime mapping, but there is no way to do this directly using broadcast.
If your collection always contains DateTime objects, you can simply do:
ICollection<DateTime> collection = ...;
collection.Sort(Comparer<DateTime>.Default); // or just collection.Sort()
Edit after reading the comment:
If you are working with ICollection directly, you can use the LINQ option:
collection.Cast<DateTime>().OrderBy( date => date );
If you are working with something that implements IList<T>
(for example List<DateTime>
), you can simply call Sort () on the list itself.
Since you are using a non-standard class, you need to make your own comparator:
class Comparer : IComparer<object> {
int Compare(object a, object b) {
return DateTime.Compare((DateTime)a, (DateTime)b);
}
}
Then you can call:
collection.Sort(new Comparer() );
If you can change the type of the collection object (i.e. to ArrayList
from List<object>
), you can simply use a non-generic interface IComparer
(which is implemented Comparer<DateTime>.Default
).
If you can't change the type of the collection object, you're out of luck. (You can always implement an object that implements IComparer<object>
, for example:
public class DateTimeComparer : IComparer<object>
{
public int Compare(object x, object y)
{
IComparer myComparer = Comparer<DateTime>.Default as IComparer;
return myComparer.Compare(x, y);
}
}
(This will throw an exception if you have any non-DateTime items in your collection)
EDIT:
You can also implement something a little more secure, i.e .:
public class DateTimeComparer : IComparer<object>
{
public int Compare(object x, object y)
{
if ( x is DateTime && y is DateTime )
{
return Comparer<DateTime>.Default.Compare((DateTime) x, (DateTime) y);
}
else
{
// handle the type mismatch
}
}
}
You can define an extension function like this:
public static class ComparerConversion
{
private class ComparerWrapper<T> : IComparer<object>
{
private readonly IComparer<T> comparer;
public ComparerWrapper(IComparer<T> comparer)
{
this.comparer = comparer;
}
public int Compare(object x, object y)
{
return this.comparer.Compare((T)x, (T)y);
}
}
public static IComparer<object> ToObjectComparer<T>(this IComparer<T> comparer)
{
return new ComparerWrapper<T>(comparer);
}
}
and use it like this:
List<object> collection = new List<object> { DateTime.Now.AddDays(1), DateTime.Now };
collection.Sort(Comparer<DateTime>.Default.ToObjectComparer());
Just try removing the cast and letting the compiler choose IComparer
instead IComparer<T>
.
Comparer<T>
implements both IComparer<T>
and IComparer
so it should work.
This works great:
ArrayList collection = new ArrayList {DateTime.Now.AddDays(1), DateTime.Now};
collection.Sort(Comparer<DateTime>.Default);