C# 中distinct的使用

假设我们有一个类: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();

转载于:https://www.cnblogs.com/joyang/p/5702472.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值