当前位置: 编程技术>.net/c#/asp.net
为何Linq的Distinct实在是不给力
来源: 互联网 发布时间:2014-10-20
本文导语: 假设我们有一个类:Productpublic class Product{ public string Id { get; set; } public string Name { get; set; }}Main函数如下:static void Main(){ List products = new List() { new Product(){ Id="1", Name="n1"}, new Product(){ Id="1", ...
假设我们有一个类:Product
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
Main函数如下:
static void Main()
{
List products = new List()
{
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 的结果是:
因为Distinct 默认比较的是Product对象的引用,所以返回4条数据。
那么如果我们希望返回Id唯一的product,那么该如何做呢?
Distinct方法还有另一个重载:
//通过使用指定的 System.Collections.Generic.IEqualityComparer 对值进行比较
//返回序列中的非重复元素。
public static IEnumerable Distinct(this IEnumerable source,
IEqualityComparer comparer);
该重载接收一个IEqualityComparer的参数。
假设要按Id来筛选,那么应该新建类ProductIdComparer 内容如下:
public class ProductIdComparer : IEqualityComparer
{
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());
结果如下:
现在假设我们要 按照 Name来筛选重复呢?
很明显,需要再添加一个类ProductNameComparer.
那能不能使用泛型类呢??
新建类PropertyComparer 继承IEqualityComparer 内容如下:
public class PropertyComparer : IEqualityComparer
{
private PropertyInfo _PropertyInfo;
///
/// 通过propertyName 获取PropertyInfo对象
///
///
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 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("Id"));
var distinctProduct = products.Distinct(new PropertyComparer("Name"));
结果如下:
为什么微软不提供PropertyEquality 这个类呢?
按照上面的逻辑,这个类应该没有很复杂啊,细心的同学可以发现PropertyEquality 大量的使用了反射。每次获取属性的值的时候,都在调用
_PropertyInfo.GetValue(x, null);
可想而知,如果要筛选的记录非常多的话,那么性能无疑会受到影响。
为了提升性能,可以使用表达式树将反射调用改为委托调用,
具体代码如下:
public class FastPropertyComparer : IEqualityComparer
{
private Func getPropertyValueFunc = null;
///
/// 通过propertyName 获取PropertyInfo对象
///
///
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(me, expPara).Compile();
}
#region IEqualityComparer 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("Id")).ToList();
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
Main函数如下:
static void Main()
{
List products = new List()
{
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 的结果是:
因为Distinct 默认比较的是Product对象的引用,所以返回4条数据。
那么如果我们希望返回Id唯一的product,那么该如何做呢?
Distinct方法还有另一个重载:
//通过使用指定的 System.Collections.Generic.IEqualityComparer 对值进行比较
//返回序列中的非重复元素。
public static IEnumerable Distinct(this IEnumerable source,
IEqualityComparer comparer);
该重载接收一个IEqualityComparer的参数。
假设要按Id来筛选,那么应该新建类ProductIdComparer 内容如下:
public class ProductIdComparer : IEqualityComparer
{
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());
结果如下:
现在假设我们要 按照 Name来筛选重复呢?
很明显,需要再添加一个类ProductNameComparer.
那能不能使用泛型类呢??
新建类PropertyComparer 继承IEqualityComparer 内容如下:
public class PropertyComparer : IEqualityComparer
{
private PropertyInfo _PropertyInfo;
///
/// 通过propertyName 获取PropertyInfo对象
///
///
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 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("Id"));
var distinctProduct = products.Distinct(new PropertyComparer("Name"));
结果如下:
为什么微软不提供PropertyEquality 这个类呢?
按照上面的逻辑,这个类应该没有很复杂啊,细心的同学可以发现PropertyEquality 大量的使用了反射。每次获取属性的值的时候,都在调用
_PropertyInfo.GetValue(x, null);
可想而知,如果要筛选的记录非常多的话,那么性能无疑会受到影响。
为了提升性能,可以使用表达式树将反射调用改为委托调用,
具体代码如下:
public class FastPropertyComparer : IEqualityComparer
{
private Func getPropertyValueFunc = null;
///
/// 通过propertyName 获取PropertyInfo对象
///
///
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(me, expPara).Compile();
}
#region IEqualityComparer 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("Id")).ToList();
您可能感兴趣的文章:
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。