程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> Linq的Distinct太不給力了

Linq的Distinct太不給力了

編輯:C#入門知識

 

假設我們有一個類: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();

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved