Linq sorting object by custom property via reflection
Got a Customer
class Country
that has a property that has a string property Name
. Also Customer
implements IComparable<Country>
like this:
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
Now:
var custList = new List<Customer>{...};
custList.OrderBy(cust => cust.Country).ToList(); //Sorts as charm.
And if you try to sort through reflection:
var itemProp = typeof(Customer).GetProperty("Country");
custList = c.Customers.ToList()
.OrderBy(cust => itemProp.GetValue(cust, null)).ToList(); // Fails
Throws an exception "At least one object must implement IComparable"
Please explain why this is happening and how to correctly sort the client by custom property through reflection. Thank.
source to share
Since it GetValue
returns Object
, you need to implement a non-generic version IComparable
.
void Main()
{
var custList = new List<Customer>()
{
new Customer(){ Country = new Country(){ Name = "Sweden" } },
new Customer(){ Country = new Country(){ Name = "Denmark" } },
};
var itemProp = typeof(Customer).GetProperty("Country");
custList = custList.OrderBy(cust => itemProp.GetValue(cust, null)).ToList();
custList.Dump();
}
public class Country : IComparable<Country>, IComparable
{
public string Name {get;set;}
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
public int CompareTo(object other)
{
var o = other as Country;
if(o == null)
return 0; //Or how you want to handle it
return CompareTo(o);
}
}
public class Customer
{
public Country Country{get;set;}
}
source to share
Assuming the base type is correct (i.e. Country
), you should do this while it Country
implements IComparable
:
Here's an example of a console application that works correctly (note that there is no error handling):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Number: IComparable<Number>, IComparable
{
public Number(int value)
{
Value = value;
}
public readonly int Value;
public int CompareTo(Number other)
{
return Value.CompareTo(other.Value);
}
public int CompareTo(object obj)
{
return CompareTo((Number) obj);
}
}
class Test
{
public Number Number;
public object Obj
{
get { return Number; }
}
public override string ToString()
{
return Number.Value.ToString();
}
}
internal static class Program
{
static void Main()
{
var itemProp = typeof(Test).GetProperty("Obj");
Console.WriteLine(string.Join("\n",
data().OrderBy(x => itemProp.GetValue(x, null))));
}
static IEnumerable<Test> data()
{
for (int i = 0; i < 10; ++i)
yield return new Test {Number = new Number(10-i)};
}
}
}
source to share