How to compare c# generic type in Mono 2.8.x

Hi all,

I have a template class in C++ like that:

template <class T> class MyRect
{
   public:
      T mLeft;
      T mRight;

      T GetWidth() const
      {
         return mRight > mLeft ? (mRight - mLeft) : (mLeft - mRight);
      }
}

And when I translate it into C#, the Mono’s compiler tell me that I can’t apply operator ‘>’ on the T and T type. what should I do in this case?

Thanks a lot.

Here’s the right translation, you don’t need the template keyword anymore.

public class MyRect<T>
{
    public T mLeft;
    public T mRight;
     
    public T GetWidth()
    {
       return mRight > mLeft ? (mRight - mLeft) : (mLeft - mRight);
    }
}

I assume that you have overloaded the - operator for this to work, right?

EDIT: I re-read your title, kinda got mislead, thought you were talking about the ‘<’ that’s after/before the T.

You need to implement your own way of comparing, by overloading the operators you need. For example:

public class Dimension2D
{
	public float x;
	public float y;
	public Dimension2D(float x = 0, float y = 0) { this.x = x; this.y = y; }

	public static Dimension2D operator *(Dimension2D d, float n)
	{
		return new Dimension2D(d.x * n, d.y * n);
	}

	public static Dimension2D operator +(Dimension2D dim1, Dimension2D dim2)
	{
		return new Dimension2D(dim1.x + dim2.x, dim1.y + dim2.y);
	}

	public static Dimension2D operator -(Dimension2D dim1, Dimension2D dim2)
	{
		return new Dimension2D(dim1.x - dim2.x, dim1.y - dim2.y);
	}

	public static bool operator ==(Dimension2D d1, Dimension2D d2)
	{
		if (ReferenceEquals(d1, null) && ReferenceEquals(d2, null))
			return true;
		if (ReferenceEquals(d1, null) || ReferenceEquals(d2, null))
			return false;
		return (d1.x == d2.x && d1.y == d2.y);
	}

	public static bool operator !=(Dimension2D d1, Dimension2D d2)
	{
		return !(d1 == d2);
	}

	public static bool operator >(Dimension2D d, float n)
	{
		return (d.x > n && d.y > n);
	}

	public static bool operator <(Dimension2D d, float n)
	{
		return (d.x < n && d.y < n);
	}

	public static bool operator >(Dimension2D d1, Dimension2D d2)
	{
	   return (d1.x > d2.x && d1.y > d2.y);
	}

	public static bool operator <(Dimension2D d1, Dimension2D d2)
	{
	   return (d1.x < d2.x && d1.y < d2.y);
	}

}

Doesn’t matter if it was a class/struct. Note that you can’t overload just ‘<’ and leave ‘>’, or ‘==’ without ‘!=’, but you can overload ‘-’, without ‘+’, and ‘*’ without ‘/’. Now, I could do this:

Dimension2D d1 = new Dimension2D(Random.Range(0, 10), Random.Range(0, 10));
if (d1 > 5)
   // do something

Dimension2D d2 = d1 + new Dimension2D(5, 10);

if (d1 > d2) {
   Dimension2D d3 = d2 - d1 * d1;

   if (d1 == d3 / d2)
      // do something
}

EDIT: Looking more at your question, for your case to work, the type ‘T’ must implement System.IComparable interface, and then you declare your class like this:

public class MyRect<T> where T: System.IComparable
{
    public T mLeft;
    public T mRight;
     
    public T GetWidth()
    {
       return mRight.CompareTo(mLeft) > 0 ? (mRight - mLeft) : (mLeft - mRight);
    }
}

Then, in my example, all I have to change in my Dimension2D class, is make it implement the IComparable interface, and implement the CompareTo method:

public class Dimension2D : System.IComparable
{
   // Stuff...
   public int CompareTo(object obj)
   {
       Dimension2D d = (Dimension2D)obj;
       if (d == null) return -1;
       if (this > d)
          return 1;
       else if (this < d)
          return -1;
       else return 0;
   }
   // Stuff...
}

Now, T could be anything, as long as it implements the IComparable interface, which means I could do this:

MyRect<int> rInt = new MyRect<int>();
rInt.mLeft = value1;
rInt.mRight = value2;
if (rInt.GetWidth() == 10)
  // do something

Or

MyRect<Dimension2D> rDim = new MyRect<Dimension2D>();
rDim.mLeft = value1;
rDim.mRight = value2;
Dimension2D w = rDim.GetWidth();
if (w > value3)
  // do something
else // do something else

This is valid because int and Dimension2D both implement IComparable :slight_smile:

EDIT: One more thing is missing, you will get an error in the GetWidth method, this makes sense cause the compiler got no way of telling that T has - and + overloaded. A work-around this, is to create an IOperatable interface:

	public interface IOperatable<T>
	{
		T Add(T other);
		T Subtract(T other);
	}

And then:

public class Dimension2D : System.IComparable, IOperatable
{
   // Stuff...
   Dimension2D Add(Dimension2D d)
   {
       return this + d;
   }

   Dimension2D Subtract(Dimension2D d)
   {
       return this - d;
   }
   // Stuff...
}

Then, your GetWidth will be:

T GetWidth()
{
    return mRight.CompareTo(mLeft) > 0 ? mRight.Subtract(mLeft) : mLeft.Subtract(mRight);
}

But the problem is, now you can’t do:

MyRect<int> rInt = new MyRect<int>();

Cause int doesn’t implement IOperatable :confused:

I will be looking for the best way around this, and will update accordingly.

EDIT: See Marc’s answer here, for the operators problem, looks like it’s not as easy as I thought to get the behavior that we want.