Reflection is a great tool when it comes to handling the information at runtime. I found this solution below to be suitable in one of my applications because I wanted something that I could easily adapt.

The implementation of the method below looks for both public and private fields and properties.

  1. namespace MyNamespace
  2. {
  3. static class Extensions
  4. {
  5. public static IEnumerable<T> DistinctBy<T, Tkey>(this IEnumerable<T> @this, string binding)
  6. {
  7. string[] PropertyNames = binding.Split('.');
  8. IList<Tkey> PropertyValues = new List<Tkey>();
  9. IList<T> DistinctList = new List<T>();
  10. //
  11. foreach (var item in @this)
  12. {
  13. //get key
  14. object Key = item;
  15. foreach (var Property in PropertyNames)
  16. {
  17. Type keyType = Key.GetType();
  18. //Check for property first - public and private
  19. FieldInfo fieldInfo = keyType.GetField(Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  20. PropertyInfo propertyInfo = keyType.GetProperty(Property, BindingFlags.Public | BindingFlags.NonPublic |BindingFlags.Instance);
  21. if (propertyInfo != null)
  22. {
  23. Key = propertyInfo.GetValue(Key);
  24. }
  25. else if (fieldInfo != null)
  26. {
  27. Key = fieldInfo.GetValue(Key);
  28. }
  29. else {
  30. Key = null;
  31. }
  32. }
  33. if (Key == null) {
  34. DistinctList.Add(item);
  35. }
  36. else if (PropertyValues.Where(x => x.Equals(Key)).Count() == 0)
  37. {
  38. PropertyValues.Add((Tkey)Key);
  39. DistinctList.Add(item);
  40. }
  41. }
  42. return DistinctList;
  43. }
  44. }

Let’s create a product class that has a Price. We aim to easily create a list with unique “parameters” that will be defined in a string.

  1. class Price
  2. {
  3. public double Value { get; set; }
  4. }
  5. class Product
  6. {
  7. public string Name { get; set; }
  8. public Price Price { get; set; }
  9. private int NoPrice;
  10. }
  11. class Program
  12. {
  13. static void Main(string[] args)
  14. {
  15. List<Product> Products = new List<Product>
  16. {
  17. new Product{ Name = "Product1", Price = new Price{ Value = 100} },
  18. new Product{ Name = "Product2", Price = new Price{ Value = 100} },
  19. new Product{ Name = "Product3", Price = new Price{ Value = 200} }
  20. };
  21. // will result 1 item, NoPrice default for all is 0
  22. var DistinctProducts1 = Products.DistinctBy<Product, int>("NoPrice");
  23. // will result 3 items, no such key is found
  24. var DistinctProducts2 = Products.DistinctBy<Product, int>("NoPrice.Test");
  25. // will result 2 items - Value is 100 for 2 of them
  26. var DistinctProducts3 = Products.DistinctBy<Product, int>("Price.Value");
  27. }
  28. }
  29. }