Click here to Skip to main content
15,891,136 members
Articles / Programming Languages / C#

Sorting Collections by Multiple Properties

Rate me:
Please Sign up or sign in to vote.
4.28/5 (21 votes)
9 Sep 2008CPOL7 min read 82.5K   758   40  
How to sort collections by properties using a SQL-like expression
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace NVivo.Sorting
{
    /// <summary>
    /// This class compares objects using a property value.
    /// </summary>
    /// <typeparam name="T">The type of class to compare.</typeparam>
    [DebuggerDisplay("{GetDebuggerText()}")]
    public class PropertyComparer<T> : IComparer<T>
    {
        private string _propertyName;
        private MethodInfo _getMethod;
        private Comparer _comparer;

        /// <summary>
        /// Creates a new PropertyComparer using a default Comparer.
        /// </summary>
        /// <param name="propertyName">The name of the property to compare.</param>
        /// <
        public PropertyComparer(string propertyName)
            : this(propertyName, null)
        { }

        /// <summary>
        /// Creates a new PropertyComparer.
        /// </summary>
        /// <param name="propertyName">The name of the property to compare.</param>
        /// <param name="valueComparer">A Comparer to compare the property value.</param>
        public PropertyComparer(string propertyName, Comparer valueComparer)
        {
            ExtractMethod(typeof(T), propertyName);

            if (valueComparer != null)
                _comparer = valueComparer;
            else
                _comparer = Comparer.Default;
        }

        /// <summary>
        /// Extracts the "get" accessor from the type.
        /// </summary>
        private void ExtractMethod(Type t, string propertyName)
        {
            // gets the property
            PropertyInfo pi = t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);

            if (pi == null)
                throw new InvalidOperationException("Cannot find a instance property named '" + _propertyName + "'.");

            // sets the property name to use in the debugger
            _propertyName = pi.Name;

            // gets the "get" acessor for the property
            MethodInfo mi = pi.GetGetMethod();

            if (mi == null)
                throw new InvalidOperationException("The property '" + _propertyName + "' does not have a get accessor.");

            _getMethod = mi;
        }

        /// <summary>
        /// Compares two object's properties.
        /// </summary>
        public int Compare(T x, T y)
        {
            object xValue = _getMethod.Invoke(x, null);
            object yValue = _getMethod.Invoke(y, null);

            return _comparer.Compare(xValue, yValue);
        }

        // this method is used to display a more descriptive text in the debugger
        private string GetDebuggerText()
        {
            return String.Format("{0}.{1}", _getMethod.DeclaringType.Name, _propertyName);
        }
    }
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions