Generics and Implementing IComparable
Asked Answered
G

2

9

I am very new to generics and I am trying to write a simple class which will be generic but also allow sorting of some description on a string member variable.

At the moment I have a basic class but when I try to implement the interface member CompareTo() I get an error at the top telling me it is not implemented. What is the issue here?

using System;

namespace GenericsPracticeConsole.Types
{
    class SortableGenericType<T> : IComparable
    {
        private T t;
        private string stringName;

        public T name
        {
            get { return t; }
            set { t = value; }
        }

        public int CompareTo(SortableGenericType<T> ourObject)
        {
            return stringName.CompareTo(ourObject.stringName);
        }
    }
}
Gynaecomastia answered 1/2, 2012 at 10:26 Comment(0)
S
9

There are two interfaces IComparable and IComparable<U>. IComparable is the older one (that came before generics) which requires instances to be compared with arbitrary objects. IComparable<U> requires instances to be compared with instances of U. If you want to declare that you will compare instances of SortableGenericType on stringName fields this is what you should do:

class SortableGenericType<T> : IComparable<SortableGenericType<T>>
{
   //
}

If you also want to implement IComparable:

   class SortableGenericType<T> : IComparable, IComparable<SortableGenericType<T>> 
   {
      private string stringName;
      public T name { get; set; }

      public int CompareTo(SortableGenericType<T> ourObject)
      {
         //I forgot to add this statement:
         if(ourObject == null) 
             return -1; 
         return stringName.CompareTo(ourObject.stringName);
      }

      public int CompareTo(object obj)
      {
         if (obj.GetType() != GetType())
            return -1;
         return CompareTo(obj as SortableGenericType<T>);
      }
   }

If your class was a collection that is going to hold items of type T and you needed those items to be orderable (this is not what you ask but it is the most common scenario) than you would require T to be IComparable<T> :

   class SomeCollection<T> where T : IComparable<T>
   {
      private List<T> items; 

      private void Sort()
      {
         //
         T item1;
         T item2;
         if(item1.CompareTo(item2) < 0)
         {
            //bla bla
         }
      }
   }
Selfrighteous answered 1/2, 2012 at 10:47 Comment(10)
I wouldn't do if (obj.GetType() != GetType()) return -1;, because then your comparison is no longer antisymmetric or reflexive.Acetylate
True. But I dont know what I am supposed to do in that case. What would your suggestion be?Selfrighteous
In your second CompareTo function you use this line: return CompareTo(obj as SortableGenericType<T>); Does this line implicitly compare the current object we have with the one passed without actually referencing the current item in the line?Gynaecomastia
Ok. So my original question was with the line: return CompareTo(obj as SortableGenericType<T>); Does this line basically compare the current object that it was called on to the passed object i.e. myInstance.CompareTo(passedInstance) only without actually specifying this.CompareTo or similar? Sorry to ask again but I like to be clear that I know what is going on before continuing. Thanks for the help.Gynaecomastia
@CSharpened: your suggestion would break if someone implements a subclass SubSortableGType, and passes an argument of type SubSortableGType to CompareTo. But that reminds me that I forgot the case where ourObject might be null, I added a line for that case. Thanks. obj as SortableGenericType<T> casts obj to SortableGenericType<T>, the result is null if unsuccessful, So if you pass an argument of an unrelated type, CompareTo will be called with null and the result will always be "smaller than". Difficult to suggest anything better without knowing more about the the use cases.Selfrighteous
Sorry Ali I think you misunderstand what I am asking. All I am asking is that does the line 'return CompareTo(obj as SortableGenericType<T>);' effectively do thisObject.CompareTo(passedObject) in pseudocode. Sorry if I wasnt clear. All I want to know is whether that is the case as there is no mention of the object we called the function on.Gynaecomastia
obj as SortableGenericType<T> casts the object obj to type SortableGenericType<T> We know the cast will succeed because we checked that on the line above. So the statement calls the other CompareTo method, the one with the public int CompareTo(SortableGenericType<T> ourObject) signature, and it returns the result based on stringName fields. does this answer your question?Selfrighteous
@Ali: I'm not sure what best practice is for that. I probably just wouldn't implement IComparable :)Acetylate
@AliFerhat: A zero result from CompareTo or Compare should not be interpreted as meaning that the items being compared are truly equal, but rather that they are ranked identically relative to all other objects, an unranked relative to each other. Generally this means that only one class in an inheritance chain should implement non-generic IComparable, and all derived classes should use the inherited definition. Derived classes may implement IComparable<theirOwnType>, but should use inherited definitions for IComparable<baseTypes>.Theophrastus
The GetType() check is just....wrong. It should be like this: public int CompareTo(object obj) { var that = obj as SortableGenericType<T>; if (that == null) return -1; return this.CompareTo(that); }Fen
M
6

IComparable defines the method public int CompareTo(object obj). Note the parameter type - it's object, not your own type. That's why you aren't actually implementing the interface.

What you need to do is implement IComparable<SortableGenericType<T>>

Matsumoto answered 1/2, 2012 at 10:29 Comment(2)
Perfect. Thanks very much. I assumed that as my type was an object in its own right that it would be ok to do that. Thanks for explaining.Gynaecomastia
Your type is an object, but it's a specific object. IComparable allows you to compare your object to ANY object, not only to your specific type.Matsumoto

© 2022 - 2024 — McMap. All rights reserved.