首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >烦人型IEqualityComparer

烦人型IEqualityComparer
EN

Stack Overflow用户
提问于 2014-09-19 12:54:35
回答 2查看 1.1K关注 0票数 6

首先,我看到了IEqualityComparer for anonymous type,那里的答案没有回答我的问题,原因很明显,我需要一个IEqualityComparer not和IComparer来与Linq的Distinct()方法一起使用。我也查过其他的答案了,这些都不是解决办法.

问题

我有一些代码可以操作和从DataTable中提取记录。

代码语言:javascript
复制
var glext = m_dtGLExt.AsEnumerable();
var cflist =
    (from c in glext
     orderby c.Field<string>(m_strpcCCType), 
             c.Field<string>(m_strpcCC), 
             c.Field<string>(m_strpcCCDesc),
             c.Field<string>(m_strpcCostItem)
     select new
     {
        CCType = c.Field<string>(m_strpcCCType),
        CC = c.Field<string>(m_strpcCC),
        CCDesc = c.Field<string>(m_strpcCCDesc),
        CostItem = c.Field<string>(m_strpcCostItem)
     }).Distinct();

但我需要不同的方法来区分大小写。把我扔到这里的是匿名类型的使用。

尝试解决方案1

如果我有SomeClass,它有具体的对象,我显然可以做

代码语言:javascript
复制
public class SumObject
{
    public string CCType { get; set; }
    public string CC { get; set; }
    public string CCDesc { get; set; }
    public string CostItem { get; set; }
}

很明显我能做到

代码语言:javascript
复制
List<SumObject> lso = new List<SumObject>() 
{
    new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Rooney", CostItem = "I477" },
    new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Zidane", CostItem = "I677" },
    new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Falcao", CostItem = "I470" },
};
var e = lso.Distinct(new SumObjectComparer()); // Great :]

哪里

代码语言:javascript
复制
class SumObjectComparer : IEqualityComparer<SumObject>
{
    public bool Equals(SumObject x, SumObject y)
    {
        if (Object.ReferenceEquals(x, y)) 
            return true;
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;
        return x.CCType.CompareNoCase(y.CCType) == 0 && 
               x.CC.CompareNoCase(y.CC) == 0 && 
               x.CCDesc.CompareNoCase(y.CCDesc) == 0 && 
               x.CostItem.CompareNoCase(y.CostItem) == 0;
    }

    public int GetHashCode(SumObject o)
    {
        if (Object.ReferenceEquals(o, null)) 
            return 0;
        int hashCCType = String.IsNullOrEmpty(o.CCType) ? 
            0 : o.CCType.ToLower().GetHashCode();
        int hashCC = String.IsNullOrEmpty(o.CC) ? 
            0 : o.CC.ToLower().GetHashCode();
        int hashCCDesc = String.IsNullOrEmpty(o.CCDesc) ? 
            0 : o.CCDesc.ToLower().GetHashCode();
        int hashCostItem = String.IsNullOrEmpty(o.CostItem) ? 
            0 : o.CostItem.ToLower().GetHashCode();
        return hashCCType ^ hashCC ^ hashCCDesc ^ hashCostItem;
    }
}

但是,在上面的Linq查询中使用匿名类型会抛出我。

尝试解决方案2

为了尝试另一种解决方案(因为我在其他地方也有相同的问题),我生成了以下泛型比较器类

代码语言:javascript
复制
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
    Func<T, T, bool> compareFunction;
    Func<T, int> hashFunction;

    public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction)
    {
        this.compareFunction = compareFunction;
        this.hashFunction = hashFunction;
    }

    public bool Equals(T x, T y) { return compareFunction(x, y); }
    public int GetHashCode(T obj) { return hashFunction(obj); }
}

这样我就可以尝试

代码语言:javascript
复制
var comparer = new GenericEqualityComparer<dynamic>(
    (x, y) => { /* My equality stuff */ }, 
    o => { /* My hash stuff */ });

但是,这将返回的值转换为IEnumerable<dynamic>,这反过来影响了我即将使用的cflist,因此在下面的查询中,join失败。

代码语言:javascript
复制
 var cf = 
    (from o in cflist
     join od in glext
     on new { o.CCType, o.CC, o.CCDesc, o.CostItem } equals new
     {
        CCType = od.Field<string>(m_strpcCCType),
        CC = od.Field<string>(m_strpcCC),
        CCDesc = od.Field<string>(m_strpcCCDesc),
        CostItem = od.Field<string>(m_strpcCostItem)
     }
     into c
     select new { ... }

由于这段代码的大量使用,我不想进入与IEnumerable<T>之间的丑陋的转换.

问题

有办法为我的匿名类型创建我的IEquailityComparer吗?

耽误您时间,实在对不起。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-09-19 12:59:46

有办法为我的匿名类型创建我的IEquailityComparer吗?

好的。您只需要使用类型推断。例如,您可以拥有如下内容:

代码语言:javascript
复制
public static class InferredEqualityComparer
{
    public static IEqualityComparer<T> Create<T>(
        IEnumerable<T> example,
        Func<T, T, bool> equalityCheck,
        Func<T, int> hashCodeProvider)
    {
        return new EqualityComparerImpl<T>(equalityCheck, hashCodeProvider);
    }

    private sealed class EqualityComparerImpl<T> : IEqualityComparer<T>
    {
        // Implement in the obvious way, remembering the delegates and
        // calling them appropriately.
    }
}

然后:

代码语言:javascript
复制
var glext = m_dtGLExt.AsEnumerable();
var query = from c in glext
            orderby ...
            select new { ... };
var comparer = InferredEqualityComparer.Create(query,
    (x, y) => { ... },
    o => { ... }
);
var distinct = query.Distinct(comparer);

基本上,该方法的第一个参数只用于类型推断,这样编译器就可以计算出用于lambda表达式参数的类型。

您可以通过创建匿名类型的示例来提前创建比较器:

代码语言:javascript
复制
var sample = new[] { new { ... } };
var comparer = InferredExqualityComparer.Create(sample, ...);
var distinct = (... query here ... ).Distinct(comparer);

但是,每当您更改查询时,您也必须更改示例。

票数 12
EN

Stack Overflow用户

发布于 2014-09-19 13:14:14

This post可能会得到你想要的。尽管对于.NET 2.0,它也适用于较新的版本(关于如何实现这一点,请参阅本文的底部)。与Jon解决方案相比,我们不会使用像create这样的工厂方法。但我想这只是句法上的糖。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25934419

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档