Skip to content

Instantly share code, notes, and snippets.

@ianfnelson
Created March 29, 2014 19:56
Show Gist options
  • Save ianfnelson/9861758 to your computer and use it in GitHub Desktop.
Save ianfnelson/9861758 to your computer and use it in GitHub Desktop.
Code for 2006 blog post "Universal Comparer for .NET"
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Globalization;
namespace IanNelson.Utilities
{
public class UniversalComparerBase
{
#region Member Variables
private NameValueCollection mSortPropertyName = new NameValueCollection();
private NameValueCollection mSortOrder = new NameValueCollection();
#endregion
#region Constants
private const BindingFlags MemberBindingFlags = ( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase );
private const MemberTypes MemberBindingTypes = ( MemberTypes.Property | MemberTypes.Field );
private const char Delimiter = ',';
private const char SubDelimiter = ' ';
private const char PropertyDelimiter = '.';
private const string Ascending = "ASC";
private const string Descending = "DESC";
private const int ComparerEqual = 0;
#endregion
protected UniversalComparerBase( string sortExpression )
{
if ( string.IsNullOrEmpty( sortExpression.Trim() ) ) throw new ArgumentNullException( "sortExpression" );
string[] sections = sortExpression.Split( Delimiter );
string sortOrder;
foreach ( string section in sections )
{
sortOrder = Ascending;
string[] subSections = section.Trim().Split( SubDelimiter );
if ( subSections.Length > 1 ) if ( subSections[ 1 ].ToUpper( CultureInfo.InvariantCulture ).IndexOf( Descending ) != -1 ) sortOrder = Descending;
if ( subSections[ 0 ].Length > 0 )
{
mSortPropertyName.Add( subSections[ 0 ], subSections[ 0 ] );
if ( sortOrder == Ascending ) mSortOrder.Add( subSections[ 0 ], sortOrder );
}
subSections = null;
}
}
/// <summary>
/// Comparison Function
/// </summary>
/// <param name="x">Object to compare</param>
/// <param name="y">Object to compare</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order of the comparands.
/// The return value has these meanings:
/// Value Meaning Less than zero This instance is less than obj.
/// Zero This instance is equal to obj.
/// Greater than zero This instance is greater than obj.
/// </returns>
protected int ProtectedCompare( object x, object y )
{
// Setup variables
int returnValue = ComparerEqual; // Default to equal(0)
string[] properties;
object objx, objy;
// Loop through the property name sections.
foreach ( string item in this.mSortPropertyName )
{
// Reset to input objects
objx = x; objy = y;
// Get the property names.
properties = item.Split( PropertyDelimiter );
// Loop through the multi-level properties/fields
for ( int i = 0; i < properties.Length; i++ )
{
// Get the object at the next level.
MemberInfo[] memberX = objx.GetType().GetMember( properties[ i ], MemberBindingTypes, MemberBindingFlags );
MemberInfo[] memberY = objy.GetType().GetMember( properties[ i ], MemberBindingTypes, MemberBindingFlags );
if ( memberX.Length > 0 && memberY.Length > 0 )
{
objx = memberX[ 0 ].MemberType == MemberTypes.Property ? ( ( PropertyInfo )memberX[ 0 ] ).GetValue( objx, null ) : ( ( FieldInfo )memberX[ 0 ] ).GetValue( objx );
objy = memberY[ 0 ].MemberType == MemberTypes.Property ? ( ( PropertyInfo )memberY[ 0 ] ).GetValue( objy, null ) : ( ( FieldInfo )memberY[ 0 ] ).GetValue( objy );
} else throw new ArgumentException("Unable to locate objects in sort expression.");
}
IComparable ic1 = objy as IComparable; IComparable ic2 = objx as IComparable; // Cast to the IComparable interface to call the CompareTo method.
if ( ic1 != null && ic2 != null )
{
returnValue = mSortOrder[ item ] == null ? ic1.CompareTo( ic2 ) : ic2.CompareTo( ic1 );// Do the comparison
}// else throw new ArgumentException("Unable to compare objects in sort expression.");
// If the comparison is not equal(0) then we don't need to check any further.
if ( returnValue != ComparerEqual ) break;
}
return returnValue; // Return the comparison value
}
}
/// <summary>
///
/// </summary>
public class UniversalComparer : UniversalComparerBase, IComparer
{
/// <summary>
/// Constructs a comparer that will sort a collection of objects that support comparing.
/// </summary>
/// <param name="sortExpression">Supports A Sort By Statement using the same syntax as SQL.
/// For example 'CategoryName ASC, ParentCategoryId DESC'
/// Also supports multi object level sorting on fields/properties. For example :
/// 'Category.Name ASC, Parent.Id DESC'
/// 'mCategoryArray.Length ASC'</param>
public UniversalComparer( string sortExpression ) : base( sortExpression ) { }
/// <summary>
/// Comparison Function
/// </summary>
/// <param name="x">Object to compare</param>
/// <param name="y">Object to compare</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order of the comparands.
/// The return value has these meanings:
/// Value Meaning Less than zero This instance is less than obj.
/// Zero This instance is equal to obj.
/// Greater than zero This instance is greater than obj.
/// </returns>
public int Compare( object x, object y )
{
return base.ProtectedCompare( x, y );
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
public class UniversalComparer<T> : UniversalComparerBase, IComparer<T>
{
/// <summary>
/// Constructs a comparer that will sort a collection of objects that support comparing.
/// </summary>
/// <param name="sortExpression">Supports A Sort By Statement using the same syntax as SQL.
/// For example 'CategoryName ASC, ParentCategoryId DESC'
/// Also supports multi object level sorting on fields/properties. For example :
/// 'Category.Name ASC, Parent.Id DESC'
/// 'mCategoryArray.Length ASC'</param>
public UniversalComparer( string sortExpression ) : base( sortExpression ) { }
/// <summary>
/// Comparison Function
/// </summary>
/// <param name="x">Object to compare</param>
/// <param name="y">Object to compare</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order of the comparands.
/// The return value has these meanings:
/// Value Meaning Less than zero This instance is less than obj.
/// Zero This instance is equal to obj.
/// Greater than zero This instance is greater than obj.
/// </returns>
public int Compare( T x, T y )
{
return base.ProtectedCompare( x, y );
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment