Comparing Instances with IComparer

Problem
You want to know how to use IComparer to compare instances of a type that are sorted along many keys.

Solution
Create a nested type using the convention TypenameComparer that implements the IComparer interface.

Comments
Make the constructor for the comparer class take one or more enum types. One enum type can specify the fields used as sort keys, and the other enum type can specify the direction of the sort.

Example

public class Account {
    // Make these properties in a real-world application.
    public string AccountName;
    public string AccountType;

    // This enum type is used when creating an AccountComparer.
    public enum SortKey {
        AccountType,
        AccountName
    }

    // The nested comparer for Account instances.
    public class AccountComparer : IComparer {
        // You don't need public properties.
        private readonly SortKey Key;
        private readonly bool Descending;

        // Static fields that return pre-instantiated references.
        // Make it easier for developers to use the comparer class.
        public static readonly AccountComparer ByAccountType = new AccountComparer(SortKey.AccountType, false);
        public static readonly AccountComparer ByAccountName = new AccountComparer(SortKey.AccountName, false);

        public AccountComparer(SortKey key, bool descending) {
            if ( key != SortKey.AccountType && key != SortKey.AccountName ) {
                throw new ArgumentException("Invalid sort key.");
            }
            this.Key = key;
            this.Descending = descending;
        }
    }

    public int Compare(object objectOne, object objectTwo) {
        Account accountOne = (Account) objectOne;
        Account accountTwo = (Account) objectTwo;

        // Rule out simple cases.
        if ( accountOne == null && accountTwo == null ) {
            return 0;
        }
        else if ( accountOne == null ) {
            return -1;
        }
        else if ( accountTwo == null ) {
            return 1;
        }

        int result = 0;

        // For demo reasons, assume AccountName and AccountType can be null.
        switch ( this.Key ) {
            case SortKey.AccountType:
                result = accountOne.AccountType.CompareTo(accountTwo.AccountType);
                if ( result == 0 ) {
                    result = accountOne.AccountName.CompareTo(accountTwo.AccountName);
                }
                break;
            case SortKey.AccountName:
                result = accountOne.AccountName.CompareTo(accountTwo.AccountName);
                if ( result == 0 ) {
                    result = accountOne.AccountType.CompareTo(accountTwo.AccountType);
                }
                break;
            default:
                throw new ArgumentException("Invalid sort key.");
        }

        // Account for descending sort.
        if ( this.Descending ) {
            result = -result;
        return result;
        }
    }
}

Due to the AccountComparer class, it’s now simple to compare and sort Account instances.

// accounts is an array of Account objects.
Array.Sort(accounts, Account.AccountComparer.ByAccountName);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s