隐藏

C# 中distinct的使用

发布:2022/10/22 10:19:48作者:管理员 来源:本站 浏览次数:507



假设我们有一个类:Product


public class Product

{

   public string Id { get; set; }

   public string Name { get; set; }

}


Main函数如下:


static void Main()

{

   List<Product> products = new List<Product>()

   {

       new Product(){ Id="1", Name="n1"},

       new Product(){ Id="1", Name="n2"},

       new Product(){ Id="2", Name="n1"},

       new Product(){ Id="2", Name="n2"},

   };


   var distinctProduct = products.Distinct();


   Console.ReadLine();

}




可以看到distinctProduct 的结果是:


image




因为Distinct 默认比较的是Product对象的引用,所以返回4条数据。


那么如果我们希望返回Id唯一的product,那么该如何做呢?






Distinct方法还有另一个重载:


//通过使用指定的 System.Collections.Generic.IEqualityComparer<T> 对值进行比较

//返回序列中的非重复元素。

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source,


IEqualityComparer<TSource> comparer);


该重载接收一个IEqualityComparer的参数。


假设要按Id来筛选,那么应该新建类ProductIdComparer 内容如下:


public class ProductIdComparer : IEqualityComparer<Product>

{

   public bool Equals(Product x, Product y)

   {

       if (x == null)

           return y == null;

       return x.Id == y.Id;

   }


   public int GetHashCode(Product obj)

   {

       if (obj == null)

           return 0;

       return obj.Id.GetHashCode();

   }

}


使用的时候,只需要


var distinctProduct = products.Distinct(new ProductIdComparer());


结果如下:


image




现在假设我们要 按照 Name来筛选重复呢?


很明显,需要再添加一个类ProductNameComparer.


那能不能使用泛型类呢??




新建类PropertyComparer<T> 继承IEqualityComparer<T> 内容如下:


public class PropertyComparer<T> : IEqualityComparer<T>

{

   private PropertyInfo _PropertyInfo;


   /// <summary>

   /// 通过propertyName 获取PropertyInfo对象    


   /// </summary>

   /// <param name="propertyName"></param>

   public PropertyComparer(string propertyName)

   {

       _PropertyInfo = typeof(T).GetProperty(propertyName,

       BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);

       if (_PropertyInfo == null)

       {

           throw new ArgumentException(string.Format("{0} is not a property of type {1}.",

               propertyName, typeof(T)));

       }

   }


   #region IEqualityComparer<T> Members


   public bool Equals(T x, T y)

   {

       object xValue = _PropertyInfo.GetValue(x, null);

       object yValue = _PropertyInfo.GetValue(y, null);


       if (xValue == null)

           return yValue == null;


       return xValue.Equals(yValue);

   }


   public int GetHashCode(T obj)

   {

       object propertyValue = _PropertyInfo.GetValue(obj, null);


       if (propertyValue == null)

           return 0;

       else

           return propertyValue.GetHashCode();

   }


   #endregion

}






主要是重写的Equals 和GetHashCode 使用了属性的值比较。


使用的时候,只需要:


//var distinctProduct = products.Distinct(new PropertyComparer<Product>("Id"));

var distinctProduct = products.Distinct(new PropertyComparer<Product>("Name"));




结果如下:


image




为什么微软不提供PropertyEquality<T> 这个类呢?


按照上面的逻辑,这个类应该没有很复杂啊,细心的同学可以发现PropertyEquality 大量的使用了反射。每次获取属性的值的时候,都在调用

_PropertyInfo.GetValue(x, null);


可想而知,如果要筛选的记录非常多的话,那么性能无疑会受到影响。


为了提升性能,可以使用表达式树将反射调用改为委托调用,


具体代码如下:




public class FastPropertyComparer<T> : IEqualityComparer<T>

{

   private Func<T, Object> getPropertyValueFunc = null;


   /// <summary>

   /// 通过propertyName 获取PropertyInfo对象

   /// </summary>

   /// <param name="propertyName"></param>

   public FastPropertyComparer(string propertyName)

   {

       PropertyInfo _PropertyInfo = typeof(T).GetProperty(propertyName,

       BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);

       if (_PropertyInfo == null)

       {

           throw new ArgumentException(string.Format("{0} is not a property of type {1}.",

               propertyName, typeof(T)));

       }


       ParameterExpression expPara = Expression.Parameter(typeof(T), "obj");

       MemberExpression me = Expression.Property(expPara, _PropertyInfo);

       getPropertyValueFunc = Expression.Lambda<Func<T, object>>(me, expPara).Compile();

   }


   #region IEqualityComparer<T> Members


   public bool Equals(T x, T y)

   {

       object xValue = getPropertyValueFunc(x);

       object yValue = getPropertyValueFunc(y);


       if (xValue == null)

           return yValue == null;


       return xValue.Equals(yValue);

   }


   public int GetHashCode(T obj)

   {

       object propertyValue = getPropertyValueFunc(obj);


       if (propertyValue == null)

           return 0;

       else

           return propertyValue.GetHashCode();

   }


   #endregion

}




可以看到现在获取值只需要getPropertyValueFunc(obj) 就可以了。


使用的时候:


var distinctProduct = products.Distinct(new FastPropertyComparer<Product>("Id")).ToList();



http://www.cnblogs.com/joyang/p/5702472.html