Extending IEnumerable With Reflection To Create A Custom Distinct Method

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.   
  24.             // will result 3 items, no such key is found  
  25.             var DistinctProducts2 = Products.DistinctBy<Product, int>("NoPrice.Test");  
  26.   
  27.             // will result 2 items - Value is 100 for 2 of them  
  28.             var DistinctProducts3 = Products.DistinctBy<Product, int>("Price.Value");  
  29.         }  
  30.     }  
  31. }