Click here to Skip to main content
15,885,365 members
Articles / Programming Languages / C#
Article

Sorting using IComparer classes (including Generics)

Rate me:
Please Sign up or sign in to vote.
3.95/5 (9 votes)
23 Jun 2006CPOL 61.3K   409   32   8
Methodology demonstration for sorting collections by the item class properties or methods.

Sample Application screen

Introduction

I needed a methodology for sorting collections, by either properties or methods of the collection item class. I also needed to change the sort at runtime, and compare the performance of various IComparer implementations. This demo project is the result of this research.

Using the code

The source code contains this IComparer class implementation plus three others:

Generic Comparer - a class to be used for generic sorting of collection items based on a property or a method.

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace SortingMethods
{
    /// <summary>
    /// Reflection based, Generic Comparer
    /// class for in-line or instance based Sorting
    /// </summary>
    /// <example>
    /// List<DataObject> array = new List<DataObject>();
    /// 
    /// ... Code to populate list ...
    /// 
    /// array.sort(new GenericComparer<DataObject>("ID", "asc", null));
    /// 
    /// or
    /// 
    /// GenericComparer genericComparer = 
    ///       new GenericComparer<DataObject>();
    /// genericComparer.MemberName = "ID";
    /// genericComparer.SortOrder = "asc";
    /// 
    /// array.sort(genericComparer);
    /// </example>
    public class GenericComparer<T> : IComparer<T>
    {
        #region Fields
        private string memberName = string.Empty;
        private string sortOrder = string.Empty;
        private List<object> methodParameters = new List<object>();
        PropertyInfo propertyInfo = null;
        MethodInfo methodInfo = null;
        #endregion

        #region Properties
        public string MemberName
        {
            get
            {
                return memberName;
            }
            set
            {
                memberName = value;
                GetReflected();
            }
        }

        public string SortOrder
        {
            get
            {
                return sortOrder;
            }
            set
            {
                sortOrder = value;
            }
        }

        public List<object> MethodParameters
        {
            get
            {
                return methodParameters;
            }
        }
        #endregion

        #region Ctors
        /// <summary>
        /// Default constructor for use in instances.
        /// </summary>
        public GenericComparer()
        {
        }

        /// <summary>
        /// Constructor for in-line instantiation
        /// </summary>
        /// <param name="memberName">string of the member name to use for comparison
        /// can be either a method name or a property.
        /// </param>
        /// <param name="sortOrder">string of the sort order (case independent). 
        /// "ASC" for ascending order or anyting else for descending order</param>
        /// <param name="methodParameters">object array
        ///      of parameters to use for method, null otherwise.</param>
        public GenericComparer(string memberName, string sortOrder, 
               List<object> methodParameters)
        {
            this.memberName = memberName;
            this.sortOrder = sortOrder;
            this.methodParameters = methodParameters;

            GetReflected();
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Sets the global field, propertyInfo
        /// and/or memberInfo using the underlying Type
        /// </summary>
        private void GetReflected()
        {
            Type[] underlyingTypes = this.GetType().GetGenericArguments();
            Type thisUnderlyingtype = underlyingTypes[0];

            MemberInfo[] mi = thisUnderlyingtype.GetMember(memberName);
            if (mi.Length > 0)
            {
                if (mi[0].MemberType == MemberTypes.Property)
                {
                    propertyInfo = thisUnderlyingtype.GetProperty(memberName);
                }
                else if (mi[0].MemberType == MemberTypes.Method)
                {
                    Type[] signatureTypes = new Type[0];
                    if (methodParameters != null && methodParameters.Count > 0)
                    {
                        signatureTypes = new Type[methodParameters.Count];
                        for (int i = 0; i < methodParameters.Count; i++)
                        {
                            signatureTypes[i] = methodParameters[i].GetType();
                        }
                        methodInfo = 
                          thisUnderlyingtype.GetMethod(memberName, 
                          signatureTypes);
                    }
                    else
                    {
                        methodInfo = 
                          thisUnderlyingtype.GetMethod(memberName, 
                          signatureTypes);
                    }
                }
                else
                {
                    throw new Exception("Member name: " + memberName + 
                          " is not a Public Property or " + 
                          "a Public Method in Type: " + 
                          thisUnderlyingtype.Name + ".");
                }
            }
            else
            {
                throw new Exception("Member name: " + 
                          memberName + " not found.");
            }
        }

        /// <summary>
        /// Return an IComparable for use in the Compare method
        /// </summary>
        /// <param name="obj">object to get IComparable from</param>
        /// <returns>IComparable for this object</returns>
        private IComparable GetComparable(T obj)
        {
            if (methodInfo != null)
            {
                return (IComparable)methodInfo.Invoke(obj, 
                                    methodParameters.ToArray());
            }
            else
            {
                return (IComparable)propertyInfo.GetValue(obj, null);
            }

        }
        #endregion

        #region IComparer Implementation
        /// <summary>
        /// Implementing method for IComparer
        /// </summary>
        /// <param name="objOne">Object to compare from</param>
        /// <param name="objTwo">Object to compare to</param>
        /// <returns>int of the comparison, or 0 if equal</returns>
        public int Compare(T objOne, T objTwo)
        {
            IComparable iComparable1 = GetComparable(objOne);
            IComparable iComparable2 = GetComparable(objTwo);

            if (sortOrder != null && sortOrder.ToUpper().Equals("ASC"))
            {
                return iComparable1.CompareTo(iComparable2);
            }
            else
            {
                return iComparable2.CompareTo(iComparable1);
            }
        }
        #endregion
    }
}

Points of interest

The Generic Comparer and the General Comparer can be re-used in your projects "as is".

Acknowledgment

Thanks to all who have contributed comments! I have recoded the GenericComparer to create the ProgramInfo and/or MethodInfo objects in the constructor or when the MemberName property is set. The GetReflected() method uses Reflection to get the Type of the underlying object to get the PropertyInfo and/or MethodInfo.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Website: http://www.somedeveloper.us

Comments and Discussions

 
GeneralAlso [modified] Pin
Bill Pierce14-Jun-06 7:41
Bill Pierce14-Jun-06 7:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.