The last comparison library you'll ever need! Wide platform support; fluent syntax.
Install the Nito.Comparers NuGet package. By default, this includes the extension package for LINQ support.
The comparer types are in the namespace Nito.Comparers.
Let's say you've got a collection of your POCOs:
classPerson{publicstringFirstName{get;}publicstringLastName{get;}}List<Person>list= ...;Here's an easy way to sort them all by last name and then first name:
IComparer<Person>nameComparer=ComparerBuilder.For<Person>().OrderBy(p =>p.LastName).ThenBy(p =>p.FirstName);list.Sort(nameComparer);How about having Person implement it?
Let's face it: implementing comparison in .NET is a real pain. IComparable<T>, IComparable, IEquatable<T>, Object.Equals, andObject.GetHashCode?!?!
But it's easy with a base type:
classPerson:ComparableBase<Person>{staticPerson(){DefaultComparer=ComparerBuilder.For<Person>().OrderBy(p =>p.LastName).ThenBy(p =>p.FirstName);}publicstringFirstName{get;}publicstringLastName{get;}}ComparableBase<T> auto-magically implements all the comparable interfaces, including correct overrides of Object.Equals and Object.GetHashCode.
What about hash-based containers? Every single comparer produced by the Comparers library also implements equality comparison!
IEqualityComparer<Person>nameComparer=ComparerBuilder.For<Person>().OrderBy(p =>p.LastName).ThenBy(p =>p.FirstName);Dictionary<Person,Address>dict=newDictionary<Person,Address>(nameComparer);Sometimes, you can only define equality. Well, good news: there are equality comparer types that parallel the full comparer types.
classEntity:EquatableBase<Entity>{staticEntity(){DefaultComparer=EqualityComparerBuilder.For<Entity>().EquateBy(e =>e.Id);}publicintId{get;}}Sequences are sorted lexicographically. The Sequence operator takes an existing comparer for one type, and defines a lexicographical comparer for sequences of that type:
varnameComparer=ComparerBuilder.For<Person>().OrderBy(p =>p.LastName).ThenBy(p =>p.FirstName);List<IEnumerable<Person>>groups= ...;groups.Sort(nameComparer.Sequence());There's also natural extensions for LINQ that allow you to define comparers on-the-fly (particularly useful for anonymous types):
IEnumerable<Person>people= ...;varanonymousProjection=people.Select(x =>new{GivenName=x.FirstName,Surname=x.LastName});varreduced=anonymousProjection.Distinct(c =>c.EquateBy(x =>x.Surname));Need to sort dynamically at runtime? No problem!
varsortByProperties=new[]{"LastName","FirstName"};IComparer<Person>comparer=ComparerBuilder.For<Person>().Null();foreach(varpropertyNameinsortByProperties){varlocalPropertyName=propertyName;Func<Person,string>selector= p =>p.GetType().GetProperty(localPropertyName).GetValue(p,null)asstring;comparer=comparer.ThenBy(selector);}Want a cute trick? Here's one: true is "greater than" false, so if you want to order by some weird condition, it's not too hard:
// Use the default sort order (last name, then first name), EXCEPT all "Smith"s move to the head of the line.varcomparer=ComparerBuilder.For<Person>().OrderBy(p =>p.LastName=="Smith",descending:true).ThenBy(ComparerBuilder.For<Person>().Default());list.Sort(comparer);By default, null values are "less than" anything else, but you can use the same sort of trick to sort them as "greater than" non-null values (i.e., nulls will be last in a sorted collection):
List<int?>myInts= ...;varcomparer=ComparerBuilder.For<int?>().OrderBy(i =>i==null,specialNullHandling:true).ThenBy(ComparerBuilder.For<int?>().Default());myInts.Sort(comparer);// Note: we need to pass "specialNullHandling"; otherwise, the default null-ordering rules will apply.For full details, see the detailed docs.
Other languages provide a comparison operator <=>, which is called the "spaceship operator". This library provides similar capabilities for C#, hence the "spaceship logo".