Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using IComparer<T>.Compare(T,T) in C#

Tags:

c#

icomparer

I've been trying to make a generic Reverse Priority Queue, but int the EnQueue, but I still can't manage the errors that come with using IComparer.

The Error: Error 1 An object reference is required for the non-static field, method, or property 'System.Collections.Generic.IComparer.Compare(T, T)'

public void inQ(T dat)//adding element in place, increasing order
    {
        if (start == null)//that means that the RPQ is empty
        {
            start = new node(dat);
            return;
        }
        if (IComparer<T>.Compare(start.data, dat) > 0)//Doesn't work
        {
            start = new node(dat, start);
            return;
        }
        //Default Case
        //no need for an else, actually
        node q = start;
        while (q.next != null && Comparer<T>.Default.Compare(q.next.data, dat) < 0)//Works Perfectly
            q++;
        q.next = new node(dat, q.next);
    }
like image 622
Sherbi7y Avatar asked Dec 12 '22 02:12

Sherbi7y


1 Answers

What happened

you need an instance of IComparer<T> to be able to call the method Compare on it... remember this is an interface, it doesn't have static methods.

That's why this doesn't work:

IComparer<T>.Compare(start.data, dat) > 0

On the other hand Comparer<T> is a class and it offers you an static property Default that gives you an instance of IComparer<T>...

That's why this works:

Comparer<T>.Default.Compare(q.next.data, dat) < 0

Getting an instance of IComparer<T>

You can consider storing a field with your instance of IComparer<T> and using that instead. That will minimize confusion, and also allow you to get an IComparer<T> in the constructor to be stored on that field - which is useful in case the client wants a custom behaviour.


The default instance

If you want to use the default comparer you can get it from it:

var comparer = Comparer<T>.Default;

This comparer will give the default behaviour for whatever T you use. This behaviour is equivalent to calling CompareTo on the instances of T, with extra handling for nulls. Since you cannot use the method CompareTo when your intance is null you may want to check for null before calling CompareTo... using a comparer removes the problem.


Implementing IComparer<T>

Since IComparer<T> is an interface, you can write a class to implement it, that class must have a Compare method with whatever logic you want. Then you create an instance of that class and use it where the system wants an IComparer<T>.

See the an example of implementing IComparer<T> on MSDN

// This class is not demonstrated in the Main method 
// and is provided only to show how to implement 
// the interface. It is recommended to derive 
// from Comparer<T> instead of implementing IComparer<T>. 
public class BoxComp : IComparer<Box>
{
    // Compares by Height, Length, and Width. 
    public int Compare(Box x, Box y)
    {
        if (x.Height.CompareTo(y.Height) != 0)
        {
            return x.Height.CompareTo(y.Height);
        }
        else if (x.Length.CompareTo(y.Length) != 0)
        {
            return x.Length.CompareTo(y.Length);
        }
        else if (x.Width.CompareTo(y.Width) != 0)
        {
            return x.Width.CompareTo(y.Width);
        }
        else
        {
            return 0;
        }
    }
}

Your comparer would be an instance of the class:

var comparer = new BoxComp();

Note: the documentation actually suggest to inherit from Comparer<T> instead of implementing IComparer<T> directly, the pragmatic reason is that Comparer<T> also implements IComparer in addition of IComparer<T>.


Creating an IComparer<T> with a delegate

In case you are not familiar with what a delegate is, let's say it is a reference to a method, so you can have a variable that has a reference to method and pass it around.

You can create an IComparer<T> if you have a delegate to a method that does the comparison... this is done by calling the method Comparer<T>.Create witch will take a delegate to the method you want. For example you can use a lambda expresion as follows:

var comparer = Comparer<string>.Create
(
    (str1, str2) => str1.Length.CompareTo(str2.Length)
);

The code above is a shorthand for this:

Comparison<string> comparison = (str1, str2) => str1.Length.CompareTo(str2.Length);
var comparer = Comparer<string>.Create(comparison);

Which in turn is a shorthand for this:

Comparison<string> comparison = delegate(string str1, string str2)
{
   return str1.Length.CompareTo(str2.Length);
};
var comparer = Comparer<string>.Create(comparison);

Which is sugar for something like this (except that in the above code the method is anonymous):

Comparison<string> comparison = StringComparison;
var comparer = Comparer<string>.Create(comparison);

// ...

private static int StringComparison(string str1, string str2)
{
    return str1.Length.CompareTo(str2.Length);
}

Extra readings:

  • What's the Comparer class for?
  • C#/.NET Little Wonders: Comparer.Default
  • How to use the IComparable and IComparer interfaces in Visual C#
  • How to Implement IComparable Interface in Base and Derived Classes
like image 81
Theraot Avatar answered Jan 27 '23 16:01

Theraot