当前位置: 移动技术网 > IT编程>开发语言>c# > 为何Linq的Distinct实在是不给力

为何Linq的Distinct实在是不给力

2019年07月18日  | 移动技术网IT编程  | 我要评论
假设我们有一个类:productpublic class product{    public string id { get; set; }
假设我们有一个类: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();

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网