当前位置:  开发笔记 > 编程语言 > 正文

你能用lambda表达式创建一个简单的'EqualityComparer <T>'

如何解决《你能用lambda表达式创建一个简单的'EqualityComparer<T>'》经验,为你挑选了2个好方法。

重要提示:这不是LINQ-to-SQL问题.这是对象的LINQ.

简短的问题:

LINQ中是否有一种简单的方法可以根据对象的键属性从列表中获取不同的对象列表.

长问题:

我正在尝试对具有键作为其属性之一的对象Distinct()列表执行操作.

class GalleryImage {
   public int Key { get;set; }
   public string Caption { get;set; }
   public string Filename { get; set; }
   public string[] Tags {g et; set; }
}

我有一个Gallery包含的对象列表GalleryImage[].

由于web服务的工作方式[原文如此],我有GalleryImage对象的重复 .我认为用它Distinct()来获得一个独特的列表是一件简单的事情.

这是我想要使用的LINQ查询:

var allImages = Galleries.SelectMany(x => x.Images);
var distinctImages = allImages.Distinct(new 
                     EqualityComparer((a, b) => a.id == b.id));

问题是这EqualityComparer是一个抽象类.

我不想:

实现IEquatable,GalleryImage因为它是生成的

必须编写一个单独的类来实现IEqualityComparer,如下所示

是否有一个EqualityComparer我缺少的具体实现?

我原本以为会有一种简单的方法可以根据键从一组中获取"不同"的对象.



1> Jon Skeet..:

(这里有两种解决方案 - 见第二种解决方案):

我的MiscUtil库有一个ProjectionEqualityComparer类(以及两个支持类来使用类型推断).

以下是使用它的示例:

EqualityComparer comparer = 
    ProjectionEqualityComparer.Create(x => x.id);

这是代码(删除了评论)

// Helper class for construction
public static class ProjectionEqualityComparer
{
    public static ProjectionEqualityComparer
        Create(Func projection)
    {
        return new ProjectionEqualityComparer(projection);
    }

    public static ProjectionEqualityComparer
        Create (TSource ignored,
                               Func projection)
    {
        return new ProjectionEqualityComparer(projection);
    }
}

public static class ProjectionEqualityComparer
{
    public static ProjectionEqualityComparer
        Create(Func projection)
    {
        return new ProjectionEqualityComparer(projection);
    }
}

public class ProjectionEqualityComparer
    : IEqualityComparer
{
    readonly Func projection;
    readonly IEqualityComparer comparer;

    public ProjectionEqualityComparer(Func projection)
        : this(projection, null)
    {
    }

    public ProjectionEqualityComparer(
        Func projection,
        IEqualityComparer comparer)
    {
        projection.ThrowIfNull("projection");
        this.comparer = comparer ?? EqualityComparer.Default;
        this.projection = projection;
    }

    public bool Equals(TSource x, TSource y)
    {
        if (x == null && y == null)
        {
            return true;
        }
        if (x == null || y == null)
        {
            return false;
        }
        return comparer.Equals(projection(x), projection(y));
    }

    public int GetHashCode(TSource obj)
    {
        if (obj == null)
        {
            throw new ArgumentNullException("obj");
        }
        return comparer.GetHashCode(projection(obj));
    }
}

二解决方案

要为Distinct执行此操作,您可以DistinctBy在MoreLINQ中使用扩展名:

    public static IEnumerable DistinctBy
        (this IEnumerable source,
         Func keySelector)
    {
        return source.DistinctBy(keySelector, null);
    }

    public static IEnumerable DistinctBy
        (this IEnumerable source,
         Func keySelector,
         IEqualityComparer comparer)
    {
        source.ThrowIfNull("source");
        keySelector.ThrowIfNull("keySelector");
        return DistinctByImpl(source, keySelector, comparer);
    }

    private static IEnumerable DistinctByImpl
        (IEnumerable source,
         Func keySelector,
         IEqualityComparer comparer)
    {
        HashSet knownKeys = new HashSet(comparer);
        foreach (TSource element in source)
        {
            if (knownKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }

在这两种情况下,ThrowIfNull看起来像这样:

public static void ThrowIfNull(this T data, string name) where T : class
{
    if (data == null)
    {
        throw new ArgumentNullException(name);
    }
}



2> kvb..:

在Charlie Flowers的答案的基础上,您可以创建自己的扩展方法来执行想要的操作,该方法内部使用分组:

    public static IEnumerable Distinct(
        this IEnumerable seq, Func getKey)
    {
        return
            from item in seq
            group item by getKey(item) into gp
            select gp.First();
    }

您还可以创建一个从EqualityComparer派生的通用类,但是听起来您想避免这种情况:

    public class KeyEqualityComparer : IEqualityComparer
    {
        private Func GetKey { get; set; }

        public KeyEqualityComparer(Func getKey) {
            GetKey = getKey;
        }

        public bool Equals(T x, T y)
        {
            return GetKey(x).Equals(GetKey(y));
        }

        public int GetHashCode(T obj)
        {
            return GetKey(obj).GetHashCode();
        }
    }

推荐阅读
云聪京初瑞子_617
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有