Reputation: 6845
Ok so I have a number of methods that look like this:- which sorts a list by artist, album, year etc.
public void SortByAlbum(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
_list = _list.OrderBy(x => x.Album).ToList();
else if (sortOrder == SortOrder.Descending)
_list = _list.OrderByDescending(x => x.Album).ToList();
}
and this:
public void SortByArtist(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
_list = _list.OrderBy(x => x.Artist).ToList();
else if (sortOrder == SortOrder.Descending)
_list = _list.OrderByDescending(x => x.Artist).ToList();
}
Now obviously this isn't good code so it needs refactoring into one Sort() method but I just cant figure out how to do it in the simplest possible way. I don't care if it uses IComparer or LINQ.
I want it to look something like this:
public void Sort(SortOrder sortOrder, SortType sortType)
{
//implementation here
}
public enum SortType
{
Artist,
Album,
Year
}
So whats the cleanest way to do this with no code repetition?
Thanks, Lee
Upvotes: 6
Views: 3305
Reputation: 6499
I think you should add an extension method to IList<T>
:
public static class extIList {
public static IList<T> Sort<T, TKey>(this IList<T> list, SortOrder sortOrder, Func<T, TKey> keySelector) {
if (sortOrder == SortOrder.Descending) {
return list.OrderByDescending(keySelector).ToList();
} else {
return list.OrderBy(keySelector).ToList();
}
}
}
and then you can use pretty with every your objects:
IList<Person> list = new List<Person>();
list.Add(new Person("David","Beckham"));
list.Add(new Person("Gennaro","Gattuso"));
list.Add(new Person("Cristian","Carlesso"));
list = list.Sort(SortOrder.Descending, X => X.Name);
ps SortOrder already exists:
using System.Data.SqlClient;
Upvotes: 0
Reputation: 110071
Sounds like sorting is taking on a life of its own if you have several methods dedicated to it. Maybe they can be gathered into a class.
public enum SortOrder
{
Ascending = 0,
Descending = 1
}
public class Sorter<T>
{
public SortOrder Direction { get; set; }
public Func<T, object> Target { get; set; }
public Sorter<T> NextSort { get; set; }
public IOrderedEnumerable<T> ApplySorting(IEnumerable<T> source)
{
IOrderedEnumerable<T> result = Direction == SortOrder.Descending ?
source.OrderByDescending(Target) :
source.OrderBy(Target);
if (NextSort != null)
{
result = NextSort.ApplyNextSorting(result);
}
return result;
}
private IOrderedEnumerable<T> ApplyNextSorting
(IOrderedEnumerable<T> source)
{
IOrderedEnumerable<T> result = Direction == SortOrder.Descending ?
source.ThenByDescending(Target) :
source.ThenBy(Target);
return result;
}
}
Here's sample usage:
List<string> source = new List<string>()
{ "John", "Paul", "George", "Ringo" };
Sorter<string> mySorter = new Sorter<string>()
{
Target = s => s.Length,
NextSort = new Sorter<string>()
{
Direction = SortOrder.Descending,
Target = s => s
}
};
foreach (string s in mySorter.ApplySorting(source))
{
Console.WriteLine(s);
}
Output is Paul, John, Ringo, George.
Upvotes: -1
Reputation: 204129
You should be able to mimick the signature of the OrderBy extension method:
Update 1 you have to be explicit in the first generic parameter to your keySelector Func. I'm going to take a guess at your type and call it "Song".
public void Sort<TKey>(SortOrder sortOrder,
Func<Song, TKey> keySelector)
{
if (sortOrder == SortOrder.Descending)
{
_list = _list.OrderByDescending(keySelector).ToList();
}
else
{
_list = _list.OrderBy(keySelector).ToList();
}
}
Now you can call "Sort" like this:
Sort(SortOrder.Descending, x => x.Album);
Update 2
Following up on Tom Lokhorst's comment: If you want to predefine some shorthand sort criteria, you could do so by defining a class like this:
public static class SortColumn
{
public static readonly Func<Song, string> Artist = x => x.Artist;
public static readonly Func<Song, string> Album = x => x.Album;
}
Now you can simply call:
Sort(SortOrder.Descending, SortColumn.Artist);
Upvotes: 12