Today I was dealing with the problem of comparing generic types in Silverlight. After researching the problem I found many similar issues for which I couldn't find any straightforward solution on the web. So here we go - a post on the issue how to compare generic types.

First, let’s say we have a simple generic class which is composed of two generic properties A and B, and a method that we will later use to compare these two properties. Moreover, I will constraint the class to invoke the constructor of the generic type argument by using where T : new() 

    public class GenericClass<T> where T : new()  
    {  
        public GenericClass() { }  
 
        public T A { setget; }  
        public T B { setget; }  
        public T Maximum() { }  
    } 

 

For the sake of simplicity our Maximum method will just compare A and B and will return the bigger one. Now, let’s tamper with the code itself. In order to compare these two values we will rely on the Comparer<T> class that is part of the System.Windows.Generics assembly like this:

        public T Maximum()  
        {  
            Comparer<T> comparer = Comparer<T>.Default;  
        } 

 

The Default property returns a default comparer for the type specified by the generic argument. This means that our generic argument type (T), should implement the IComparable<T> interface. Later I will show you an example of how to implement such a class, but first let’s assume we will use a class that is part of the framework and implements IComparable, e.g. double or int. After we have instantiated our own comparer we can use its method Compare to compare our two generic properties:

comparer.Compare(A, B); 

 

This function will return the following integer:

 1 if A is bigger
 0 if A and B are equal,
-1 if B is bigger

Here is a simple implementation of our Maximum function:

        public T Maximum()  
        {  
            Comparer<T> comparer = Comparer<T>.Default;  
            switch (comparer.Compare(A, B))  
            {  
                case 1:  
                    return A;  
                case 0:  
                    //doesn't really matter since A equals B  
                    return A;  
                case -1:  
                    return B;  
            }  
        } 

 

Knowing that, we can easily implement several helper methods like IsBiggerThan, IsLessThan and so on, that will serve as our generic “operators”. Here is a complete implementation of this class:

    public class GenericClass<T> where T : new()  
    {  
        public GenericClass() { }  
 
        public T A { setget; }  
        public T B { setget; }  
 
        public T Maximum()  
        {  
            if (IsBiggerThan(this.A, this.B) || IsEqualTo(this.A, this.B))  
            {  
                return this.A;  
            }  
            else 
            {  
                return this.B;  
            }  
        }  
 
        private static bool Compare(T a, T b, int res)  
        {  
            Comparer<T> comparer = Comparer<T>.Default;  
            return comparer.Compare(a, b) == res ? true : false;  
        }  
 
        private static bool IsBiggerThan(T a, T b) { return Compare(a, b, 1); }  
 
        private static bool IsEqualTo(T a, T b) { return Compare(a, b, 0); }  
 
        private static bool IsLessThan(T a, T b) { return Compare(a, b, -1); }  
    } 

So we can easily use our generic class as follows:

        public void DoSomething()  
        {  
            GenericClass<double> dbl = new GenericClass<double>();  
            dbl.A = 5;  
            dbl.B = 10;  
            //maximum will be 10  
            double maximum = dbl.Maximum();  
        } 

 

So far so good. But let’s say that we want to use some custom class that should implement the IComparable<T> interface. Our test class will be called SimpleClass and will have a single property Value.

    public class SimpleClass : IComparable<SimpleClass>  
    {  
        public SimpleClass() { }  
 
        public int Value { setget; }  
 
        public int CompareTo(SimpleClass other)  
        {  
            if (this.Value > other.Value)  
            {  
                return 1;  
            }  
            if (this.Value < other.Value)  
            {  
                return -1;  
            }  
            return 0;  
        }  
    } 

 

 Having implemented the IComparable<T> interface, now the Comparer<T>.Default method will use the int CompareTo() method implemented in the derived class. Thus, we can easily be able to use the class like this:

        public void DoSomething()  
        {  
            GenericClass<SimpleClass> gen = new GenericClass<SimpleClass>();  
            gen.A = new SimpleClass() { Value = 5 };  
            gen.B = new SimpleClass() { Value = 10 };  
            //maximum will return B which has Value equal to 10  
            int maximum = gen.Maximum().Value;  
        } 

 

I hope that having such an example, currently not available in MSDN, will serve as a useful reference document.


About the Author

Valeri Hristov

Team Lead,
Platform Team

Related Posts

Comments

Comments are disabled in preview mode.