2

I have a class (Foo) which is required to compare T type objects, however T may not always implement IComparable and the constructor should be able to be used with a null comparer param. To catch this on creation of Foo I attempted the following:

public sealed class Foo<T>
{

    private readonly IComparer<T> _comparer;

    public Foo(IComparer<T> comparer)
    {
        _comparer = comparer ?? Comparer<T>.Default;
        if (_comparer == null)
            throw new NotSupportedException("A comparer was not passed for T and no default was found for T. ");

    }
}

I assumed (incorrectly) that Comparer<T>.Default would be null if the object did not implement IComparable<T> but instead Default will still return a valid Comparer which throws an ArgumentsException when compare is called and I haven't been able to find a solution through research on how to approach this situation.

How should I approach this situation?

EDIT: To clarify This class should be able to sort objects of type T using the given Comparer. But T might not always have IComparable but when a Comparer is provided then it should still be able to sort those objects constraining would break that requirement. However if the passed in Comparer is null then it should attempt to use Default, If the object is IComparable all is well if not it should throw an NotSupportedException.

  • 2
    Check MSDN page for Comparer(T).Default: https://msdn.microsoft.com/en-us/library/azhsac5f(v=vs.110).aspx. Specifically, the remarks mention this: "If type T does not implement the System.IComparable generic interface, this property returns a Comparer that uses the System.IComparable interface." – Keyur PATEL Jan 20 '17 at 06:16
  • Could something like this help in your case: http://stackoverflow.com/a/503359/6741868? Haven't tested it out. – Keyur PATEL Jan 20 '17 at 06:24
  • I made the mistake of not looking at the documentation first. :/ I did see that post on my travels but I wasn't sure it fit with what I was trying to do. Accepted answer seems to be cleaner. – BarelyTilted Jan 20 '17 at 06:50

1 Answers1

1

Base on your updated question ill give new answer.

public Foo(IComparer<T> comparer)
{
    _comparer = comparer ?? 
                     typeof(IComparable<T>).IsAssignableFrom(typeof(T)) 
                     ? Comparer<T>.Default : null;
    if (_comparer == null)
        throw new NotSupportedException("A comparer was not passed for T and no default was found for T. ");

}

How ever I don't prefer linear solution. following looks cleaner IMO

public Foo(IComparer<T> comparer)
{
    if(comparer == null)
    {
        if(typeof(IComparable<T>).IsAssignableFrom(typeof(T))
        {
             comparer = Comparer<T>.Default;
        }
        else 
             throw new NotSupportedException("A comparer was not passed for T and no default was found for T. ");
    }

    _comparer = comparer;


}
M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • This has worked like a charm, I'm using .NET-Core and apparently it is a bit more verbose I had to retrieve `TypeInfo` first `(typeof(IComparer).GetTypeInfo().IsAssignableFrom(typeof(T))` besides that. Fulfills it's purpose perfectly! – BarelyTilted Jan 20 '17 at 06:44
  • @BarelyTilted I think you got it wrong. you should use **`IComparable`** not `IComparer` :) – M.kazem Akhgary Jan 20 '17 at 06:54
  • I have indeed! I was excited it was working and didn't double check haha. Though my tests indicate both ways work for what I need. – BarelyTilted Jan 20 '17 at 06:59