IEqualityComparer的异常类型 [英] IEqualityComparer for Annoymous Type

查看:59
本文介绍了IEqualityComparer的异常类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先我看到了 IEqualityComparer 用于匿名类型 并且那里的答案没有回答我的问题,原因很明显我不需要 IEqualityComparer IComparer 来与Linq的 Distinct()方法一起使用.我也检查了其他答案,这些都无法解决...

Firstly I have seen IEqualityComparer for anonymous type and the answers there do not answer my question, for the obvious reason that I need an IEqualityComparer not and IComparer for use with Linq's Distinct() method. I have checked the other answers too and these fall short of a solution...

问题

我有一些代码可以操作和提取 DataTable

I have some code to manipulate and pull records in from a DataTable

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();

但是我需要区分大小写的独特方法.这里给我扔的是匿名类型的使用.

but I need the distinct method to be case insensitive. What is throwing me here is the use of anonymous types.

尝试解决方案1 ​​

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

If I had SomeClass which had concrete objects I could obviously do

public class SumObject
{
    public string CCType { get; set; }
    public string CC { get; set; }
    public string CCDesc { get; set; }
    public string CostItem { get; set; }
}

我显然可以做到

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 :]

其中

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查询中使用匿名类型使我大跌眼镜.

However, the use of anonymous types in the above Linq query are throwing me.

尝试解决方案2

要对此尝试另一种解决方案(并且由于在其他地方也有相同的问题),我生成了以下通用比较器类

To attempt another solution to this (and because I have the same issue elsewhere) I generated the following generic comparer class

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); }
}

以便我可以尝试

var comparer = new GenericEqualityComparer<dynamic>(
    (x, y) => { /* My equality stuff */ }, 
    o => { /* My hash stuff */ });

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

but this casts the returned value as IEnumerable<dynamic> which in turn effects my forthcoming use of cflist, so that in a following query the join fails.

 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> s之间进行丑陋的转换...

I don't want to get into ugly casting to and from IEnumerable<T>s due to the heavy use of this code...

问题

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

Is there a way I can create my an IEquailityComparer for my anonymous types?

感谢您的时间.

推荐答案

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

Is there a way I can create my an IEquailityComparer for my anonymous types?

好的.您只需要使用类型推断.例如,您可能会有类似的内容:

Sure. You just need to use type inference. For example, you could have something like:

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.
    }
}

然后:

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);

该方法的第一个参数基本上是 just 用于类型推断,以便编译器可以确定用于lambda表达式参数的类型.

Basically the first parameter to the method is just used for type inference, so that the compiler can work out what type to use for the lambda expression parameters.

可以通过创建匿名类型的样本来提前创建比较器:

You could create the comparer ahead of time by creating a sample of the anonymous type:

var sample = new[] { new { ... } };
var comparer = InferredExqualityComparer.Create(sample, ...);
var distinct = (... query here ... ).Distinct(comparer);

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

but then any time you change the query you've got to change the sample too.

这篇关于IEqualityComparer的异常类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆