Click here to Skip to main content
6,634,665 members and growing! (16,007 online)
Email Password   helpLost your password?
Languages » C# » Generics     Intermediate License: The Code Project Open License (CPOL)

Sorting using IComparer classes (including Generics)

By Jeff Bramlett

Methodology demonstration for sorting collections by the item class properties or methods.
C#.NET 2.0, Win2K, WinXPVS2005, Dev
Posted:14 Jun 2006
Updated:23 Jun 2006
Views:33,440
Bookmarked:27 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
9 votes for this article.
Popularity: 3.77 Rating: 3.95 out of 5

1

2
3 votes, 33.3%
3
1 vote, 11.1%
4
5 votes, 55.6%
5

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.

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)

About the Author

Jeff Bramlett


Member
Website: http://www.devbyjeffbramlett.com
Occupation: Software Developer (Senior)
Location: United States United States

Other popular C# articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 8 of 8 (Total in Forum: 8) (Refresh)FirstPrevNext
GeneralHow To compare Structs and Class Pinmemberraj_3272:59 20 Mar '07  
GeneralThis can be tons faster by using LCG. PinmemberMarc Brooks12:14 14 Jun '06  
GeneralRe: This can be tons faster by using LCG. PinmemberDr. Heiko Voß9:59 23 Jun '06  
GeneralRe: This can be tons faster by using LCG. PinmemberMarc Brooks12:24 23 Jun '06  
GeneralRe: This can be tons faster by using LCG. PinmemberMarc Brooks16:21 2 Oct '06  
GeneralAnother Suggestion PinmemberJames Curran8:51 14 Jun '06  
GeneralAlso [modified] PinmemberBill Pierce8:41 14 Jun '06  
GeneralSuggestion PinmemberSteve Hansen6:35 14 Jun '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 23 Jun 2006
Editor: Smitha Vijayan
Copyright 2006 by Jeff Bramlett
Everything else Copyright © CodeProject, 1999-2009
Web21 | Advertise on the Code Project