0

I am working with .Net Framework 4.8 and C# 5.0

I am using SortedSet class objects in my code. They are are defined as

SortedSet<Tuple<int, MyEnum1, MyStruct>> sortedSet1;
SortedSet<Tuple<int, MyEnum2, MyStruct>> sortedSet2;

The struct MyStruct implements a CompareTo() method.

I only wish to compare the tuples according to the first item, Item1. I can initialize these sorted sets as:

sortedSet1 = new SortedSet<Tuple<int, MyEnum1, MyStruct>>(new MyCustomComparer());

where:

private class MyCustomComparer : Comparer<Tuple<int, MyEnum1, MyStruct>>
{
    public override int Compare(Tuple<int, MyEnum1, MyStruct> x, Tuple<int, MyEnum1, MyStruct> y)
    {
        // by int value only
        return x.Item1.CompareTo(y.Item1);
    }
}

and repeat the same comparer for sortedSet2, etc.

As I may have several of these sorted sets, I wish to avoid duplicating the custom comparers. I am therefore looking for a way to use a single comparer for all these types of sorted sets. I have tried a number of approaches but I cannot seem to be able to create a single comparer that would work for all these sorted sets. So far, the closest I have found online is the following:

public class MyCustomComparer<T> : IComparer
{
   private int itemPosition;
   private int multiplier = -1;

   public MyCustomComparer(int component) : this(component, true)
   { }

   public MyCustomComparer(int component, bool descending)
   {
      if (! descending) multiplier = 1;

      if (component <= 0 || component > 6)
         throw new ArgumentException("The component argument is out of range.");

      itemPosition = component;
   }
   
   public int Compare(object x, object y)
   {       
      var tX = x as Tuple<T1, T2, T3>;
       
      if (tX == null)
      { 
         return 0;
      }   
      else
      {
         var tY = y as Tuple<T1, T2, T3>;
         switch (itemPosition)
         {
            case 1:
               return Comparer<T1>.Default.Compare(tX.Item1, tY.Item1) * multiplier;
            case 2:
               return Comparer<T2>.Default.Compare(tX.Item2, tY.Item2) * multiplier;
            case 3:
               return Comparer<T3>.Default.Compare(tX.Item3, tY.Item3) * multiplier;
            default:
               return Comparer<T1>.Default.Compare(tX.Item1, tY.Item1) * multiplier;
         }
      }
   }

This code will not work with the initialization

sortedSet1 = new SortedSet<Tuple<int, MyEnum1, MyStruct>>(new MyCustomComparer<Tuple<int, MyEnum1, MyStruct>>(1));

because the generic parameters T1, T2, T3 are unknown.

I have tried modifying the above code to

public class MyCustomComparer<T1, T2, T3> : IComparer
{ ... }

but then my initalization

sortedSet1 = new SortedSet<Tuple<int, MyEnum1, MyStruct>>(new MyCustomComparer<T1,T2,T3>(1));

fails to work. I get errors CS1502 and CS1503:

Argument 1: cannot convert from 'MyCustomComparer<int,MyEnum1,MyStruct>' to 'System.Collections.Generic.IComparer<System.Tuple<int,MyEnum1,MyStruct>>'

I have tried many other variations along these lines but nothing seems to work. I would like to think that this can be accomplished but I cannot seem to find a way to do this. Any help would be greatly appreciated. Thank you.

PBrenek
  • 561
  • 1
  • 8
  • 24
  • Why are you implementing IComparer, when you can inherit from Comparer? – Mihinator3000 Mar 22 '23 at 02:46
  • @Mihinator3000 - I couldn't get Comparer to work with my Tuple types. The only way I could think of getting it working was to have a separate Comparer for each SortedSet which I want to avoid. – PBrenek Mar 22 '23 at 04:31
  • I mean, have you tried: class MyCustomComparer : Comparer, seems to me it would work, error you are getting right now is caused by your interface not being generic – Mihinator3000 Mar 22 '23 at 04:38
  • @Mihinator3000 - I just tried it. Same issue. I can't seem to be able to pick up the items in the tuple to compare. – PBrenek Mar 22 '23 at 05:08

1 Answers1

0

In case anyone comes across this type of an issue in the future, the following is the solution I have come up with. As I am comparing on the basis of an int, I am OK with the default comparer. However, the comparisons for any of the tuple items could be customized further, as required.

Declaration:

private SortedSet<Tuple<int, MyEnum1, MyStruct1>> sortedSet1;
private SortedSet<Tuple<int, MyEnum2, MyStruct2>> sortedSet2;

Initialization:

sortedSet1 = new SortedSet<Tuple<int, MyEnum1, MyStruct1>>(new MyCustomComparer<int, MyEnum1, MyStruct1>(1));   
sortedSet2 = new SortedSet<Tuple<int, MyEnum2, MyStruct2>>(new MyCustomComparer<int, MyEnum2, MyStruct2>(1));

Comparer:

public class MyCustomComparer<T1, T2, T3> : Comparer<Tuple<T1,T2,T3>>
{
   private int itemPosition;
   private int multiplier = -1;

   public MyCustomComparer(int component) : this(component, false)
   { }

   public MyCustomComparer(int component, bool descending)
   {
      if (! descending) multiplier = 1;

      if (component <= 0 || component > 3)
         throw new ArgumentException("The component argument is out of range.");

      itemPosition = component;
   }
   
   public override int Compare(Tuple<T1, T2, T3> x, Tuple<T1, T2, T3> y)
   {
      if (x == null)
      { 
         return 0;
      }   
      else
      {
         switch (itemPosition)
         {
            case 1:
               return Comparer<T1>.Default.Compare(x.Item1, y.Item1) * multiplier;
            case 2:
               return Comparer<T2>.Default.Compare(x.Item2, y.Item2) * multiplier;
            case 3:
               return Comparer<T3>.Default.Compare(x.Item3, y.Item3) * multiplier;
            default:
               return Comparer<T1>.Default.Compare(x.Item1, y.Item1) * multiplier;
         }
      }
   }
}
PBrenek
  • 561
  • 1
  • 8
  • 24