Argument '1': cannot convert from 'System.Collections.Generic.IComparer<ToDoList.ToDoBase>'
to 'System.Collections.Generic.IComparer<ToDoList.ToDoGroup>'
Where 'ToDoBase' is the base class and 'ToDoGroup' is the derived class.
Is there a way around this, or do I have to write up a set for every derived object type?
[.net] [C#] Sorting, IComparer and inheritance
Hey there, I'm attempting to sort some List<> objects. The templated object types are different for each list, but they all have the same base class and I've written a set of IComparer classes that sort based on different attributes of the base class.
Now the compiler is throwing a whole bunch of errors at me such as:
Okay, well I just re-wrote all the compare classes to have 2 for each sorting criteria (because there are 2 derived classes).
So, I have something like 'NameAlphabeticalCompareGroup' that inherits from 'IComparer<ToDoGroup>', but I'm still getting error messages. Ones that don't make much sense to me:
The errors I'm getting for that one function call:
What is going on?
So, I have something like 'NameAlphabeticalCompareGroup' that inherits from 'IComparer<ToDoGroup>', but I'm still getting error messages. Ones that don't make much sense to me:
/** * Sort the node subtree by Name */ private void nameToolStripMenuItem1_Click(object sender, EventArgs e){ if (CheckIfCurrentTreeNodeIsValid()) Sort(ToDoList_heirarchy.SelectedNode, new NameAlphabeticalCompareGroup(), new NameAlphabeticalCompareItem());}
The errors I'm getting for that one function call:
The best overloaded method match for 'ToDoList.ToDoList_form.Sort(System.Windows.Forms.TreeNode, ref System.Collections.Generic.IComparer<ToDoList.ToDoGroup>, ref System.Collections.Generic.IComparer<ToDoList.ToDoItem>)' has some invalid argumentsArgument '2': cannot convert from 'ToDoList.NameAlphabeticalCompareGroup' to 'ref System.Collections.Generic.IComparer<ToDoList.ToDoGroup>'Argument '3': cannot convert from 'ToDoList.NameAlphabeticalCompareItem' to 'ref System.Collections.Generic.IComparer<ToDoList.ToDoItem>'
private void Sort(TreeNode node, ref IComparer<ToDoGroup> group_comp, ref IComparer<ToDoItem> item_comp){ ToDoGroup temp = (ToDoGroup)node.Tag; ProgramInstance.Sort(ref temp, ref group_comp, ref item_comp, true); // create the subtree for the treeview node again because it has changed. node = ConstructTreeViewNode(ref temp); // a change has been made ChangesSaved = false;}
What is going on?
You want to sort these todo things based on some property? Like on todo.Priority?
The simplest way to do that is to just use a little compare function, rather than a whole class:
That particular overload of list.Sort is formally declared to take a generic delegate (i.e. Comparison<T> comparer), but it will accept a function typed to take objects.
For just sorting a list, you don't need an actual object because the List won't hold onto it for use during other operations the way a Dictionary or SortedList will.
The simplest way to do that is to just use a little compare function, rather than a whole class:
{ List<SomeClass> list = new List<SomeClass>(); list.Sort(sortSomeClassFunction);}private static int sortSomeClassFunction(object o1, object o2){ SomeClass sc1 = (SomeClass)o1; SomeClass sc2 = (SomeClass)o2; // compare and return }
That particular overload of list.Sort is formally declared to take a generic delegate (i.e. Comparison<T> comparer), but it will accept a function typed to take objects.
For just sorting a list, you don't need an actual object because the List won't hold onto it for use during other operations the way a Dictionary or SortedList will.
Quote:Original post by dalep
That particular overload of list.Sort is formally declared to take a generic delegate (i.e. Comparison<T> comparer), but it will accept a function typed to take objects.
For just sorting a list, you don't need an actual object because the List won't hold onto it for use during other operations the way a Dictionary or SortedList will.
Okay, but since I'm going to be passing the function that I'm using through a couple of functions before it is used, I declared a delegate.
It gets passed like this: EventHandlerFunction -> frm.Sort -> Program.Sort
"Program" is actually a class name. It really shouldn't be but, for the moment, it is. [smile]
Okay, here's a quick sample of what I have now:
public class ComparisonFunctions { //! Define a delegate (like a definition of the function signature of the functions) for comparison functions public delegate int ComparisonFunction(object x, object y); /** * Compare the two objects. * \param x The first object to compare * \param y The second object to compare * \return Less than zero - x is less than y, Zero - x equals y, Greater than zero - x is greater than y */ public static int NameAlphabeticalCompare(object x, object y) { ToDoBase a = (ToDoBase)x; ToDoBase b = (ToDoBase)y; // return which is greater if one of the objects is null if (a == null) { if (b == null) return 0; else // because b is "greater" than a, because b exists and a does not return -1; } // if a != null and b == null else if (b == null) { // because a is "greater" than b, because a exists and b does not return 1; } // now compare the actual objects return a.Name.CompareTo(b.Name); } // other sorting functions}
And I'm using 'ComparisonFunction' as the parameter type to pass it through functions. And it works all the way up until the List.Sort function.
Argument '1': cannot convert from 'ToDoList.ComparisonFunctions.ComparisonFunction' to 'System.Collections.Generic.IComparer<ToDoList.ToDoGroup>'
Should I be using a different variation of the List.Sort function?
as dalep said,
there are two interfaces for IComparer,
IComparer
and
IComparer<T>
Dont use the templated interface, just use the old object based one.
so do:
not
Either that or implement the non generic IComparable interface on the base class, and call Sort() without any comparer.
there are two interfaces for IComparer,
IComparer
and
IComparer<T>
Dont use the templated interface, just use the old object based one.
so do:
private void Sort(TreeNode node, ref IComparer group_comp, ref IComparer item_comp)
not
private void Sort(TreeNode node, ref IComparer<ToDoGroup> group_comp, ref IComparer<ToDoItem> item_comp)
Either that or implement the non generic IComparable interface on the base class, and call Sort() without any comparer.
I have a GUI system that I wrote which has a sorter for children of any of the objects... the problem I ran in to was that the "label" object (which inherits from the base class object, where the sorter is) has its own "text" property that is only on the label object, but not the base. So if you wanted to sort a bunch of labels (like in a listbox), you'd either have to override the sorter, or you could do what I did...
I put in a "sort method" enum in, with values such as "ByName" (string name of control), "ByTagID", or "ByText". Here's the sort code (it's VB.NET, but it should translate well).
See how if the sorting method is "ByText", I specifically cast the X and Y objects in to "guiLabel", so I can grab the text value out of it? Otherwise, the objects are just "guiControlBase" objects.
The "not ... is nothing" checks are to ensure that those objects are indeed guilabel objects.
I put in a "sort method" enum in, with values such as "ByName" (string name of control), "ByTagID", or "ByText". Here's the sort code (it's VB.NET, but it should translate well).
Private Class ListComparer Implements IComparer(Of guiControlBase) Function Compare(ByVal x As guiControlBase, ByVal y As guiControlBase) As Integer Implements IComparer(Of EEGUI.guiControlBase).Compare Dim RetVal As Integer = 0 Select Case eSortingMethod Case SortingMethods.ByName RetVal = String.Compare(x.Name, y.Name) Case SortingMethods.ByTagID If x.TagID > y.TagID Then RetVal = 1 ElseIf x.TagID < y.TagID Then RetVal = -1 End If Case SortingMethods.ByTagString RetVal = String.Compare(x.TagString, y.TagString) Case SortingMethods.ByText If Not CType(x, guiLabel) Is Nothing AndAlso Not CType(y, guiLabel) Is Nothing Then RetVal = String.Compare(CType(x, guiLabel).Text, CType(y, guiLabel).Text) End If End Select If Not bSortAscending Then RetVal *= -1 End If Return RetVal End Function End Class
See how if the sorting method is "ByText", I specifically cast the X and Y objects in to "guiLabel", so I can grab the text value out of it? Otherwise, the objects are just "guiControlBase" objects.
The "not ... is nothing" checks are to ensure that those objects are indeed guilabel objects.
Quote:Original post by RipTorn
as dalep said,
there are two interfaces for IComparer,
IComparer
and
IComparer<T>
Dont use the templated interface, just use the old object based one.
so do:private void Sort(TreeNode node, ref IComparer group_comp, ref IComparer item_comp)
notprivate void Sort(TreeNode node, ref IComparer<ToDoGroup> group_comp, ref IComparer<ToDoItem> item_comp)
Either that or implement the non generic IComparable interface on the base class, and call Sort() without any comparer.
At the moment, to get it working, I have all my seperate classes again, each with 1 "int Compare(object,object)" function. I'm still getting compile errors.
I keep getting errors that say that "IComparer" cannot be converted to "IComparer<ToDoGroup>". This happens only on the call to List.Sort.
Obviously, the List's template parameter is ToDoGroup, so what am I doing wrong? Apparently List.Sort has 4 overloads, none of which take a normal "IComparer" object.
Edit:: Is it possible to do it like this, or am I going to have to have my data (ToDoGroup) classes inherit from IComparable?
Works.
I had the base class inherit from IComparable, and wrote the base::CompareTo function. I also stuck a static member variable as an enum, which describes which type of comparision to do. ie. By name, by date, etc.
Thanks for all the help, guys.
I had the base class inherit from IComparable, and wrote the base::CompareTo function. I also stuck a static member variable as an enum, which describes which type of comparision to do. ie. By name, by date, etc.
/** * The possible search criteria */ public enum SortCriteria{ Name, ///< Sort by the name DateCreated, ///< Sort by the creation date DateCompleted, ///< Sort by the completion date IsComplete, ///< Sort by the completion status DateUpdated ///< Sort by the date last updated }/** * A class to serve as an abstract base class for the ToDo group and ToDo item objects. */public class ToDoBase : IComparable{ public string Name; ///< The name of the item public DateTime DateCreated; ///< The date this item was created public DateTime DateCompleted; ///< The date this item was marked complete public bool IsComplete; ///< If the item has been marked as complete or not public ToDoGroup Parent; ///< The parent group of this item public SortCriteria SortingCriteria; ///< The criteria to sort by /** * Constructor * \param name The name of the item * \param creation_date The date the item was created */ public ToDoBase(string name, DateTime creation_date, ToDoGroup parent) { Name = name; DateCreated = creation_date; Parent = parent; } /** * Compare this object with another * \param obj The object to compare to */ public int CompareTo(object obj) { ToDoBase b = (ToDoBase) obj; if (b == null) return 1; if (SortingCriteria == SortCriteria.Name) { return this.Name.CompareTo(b.Name); } else if (SortingCriteria == SortCriteria.IsComplete) { return this.IsComplete.CompareTo(b.IsComplete); } else if (SortingCriteria == SortCriteria.DateCreated) { return this.DateCreated.CompareTo(b.DateCreated); } else if (SortingCriteria == SortCriteria.DateCompleted) { // check for invalid dates, make sure that both aren't invalid if( this.DateCompleted != b.DateCompleted ){ // if one of the dates is invalid if (this.DateCompleted == new DateTime(1, 1, 1)) return -1; else if (b.DateCompleted == new DateTime(1, 1, 1)) return 1; } return this.DateCompleted.CompareTo(b.DateCompleted); } // If sorting by updated date, only ToDoItems have an updated date. // Write checks in to make sure that only ToDoItems are attempted to sort by this criteria. else if (SortingCriteria == SortCriteria.DateUpdated) { } return 0; // just as a default, invalid value }// CompareTo}// class ToDoBase
Thanks for all the help, guys.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement