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

我可以指定内联显式类型比较器吗?

如何解决《我可以指定内联显式类型比较器吗?》经验,为你挑选了5个好方法。

因此,.NET 3.0/3.5为我们提供了许多查询,排序和操作数据的新方法,这要归功于LINQ提供的所有简洁功能.有时,我需要比较没有内置比较运算符的用户定义类型.在许多情况下,比较非常简单 - 比如foo1.key?= foo2.key.我可以使用匿名委托/ lambda函数简单地指定内联比较,而不是为该类型创建新的IEqualityComparer吗?就像是:

var f1 = ...,
    f2 = ...;
var f3 = f1.Except(
           f2, new IEqualityComparer(
             (Foo a, Foo b) => a.key.CompareTo(b.key)
           ) );

我很确定上面的内容实际上并不奏效.我只是不想让整个班级做一些"沉重"的事情只是为了告诉程序如何比较苹果和苹果.



1> Jon Skeet..:

我的MiscUtil库包含一个ProjectionComparer,用于从投影委托构建IComparer .让ProjectionEqualityComparer做同样的事情将是10分钟的工作.

编辑:这是ProjectionEqualityComparer的代码:

using System;
using System.Collections.Generic;

/// 
/// Non-generic class to produce instances of the generic class,
/// optionally using type inference.
/// 
public static class ProjectionEqualityComparer
{
    /// 
    /// Creates an instance of ProjectionEqualityComparer using the specified projection.
    /// 
    /// Type parameter for the elements to be compared
    /// Type parameter for the keys to be compared,
    /// after being projected from the elements
    /// Projection to use when determining the key of an element
    /// A comparer which will compare elements by projecting 
    /// each element to its key, and comparing keys
    public static ProjectionEqualityComparer Create(Func projection)
    {
        return new ProjectionEqualityComparer(projection);
    }

    /// 
    /// Creates an instance of ProjectionEqualityComparer using the specified projection.
    /// The ignored parameter is solely present to aid type inference.
    /// 
    /// Type parameter for the elements to be compared
    /// Type parameter for the keys to be compared,
    /// after being projected from the elements
    /// Value is ignored - type may be used by type inference
    /// Projection to use when determining the key of an element
    /// A comparer which will compare elements by projecting
    /// each element to its key, and comparing keys
    public static ProjectionEqualityComparer Create
        (TSource ignored,
         Func projection)
    {
        return new ProjectionEqualityComparer(projection);
    }

}

/// 
/// Class generic in the source only to produce instances of the 
/// doubly generic class, optionally using type inference.
/// 
public static class ProjectionEqualityComparer
{
    /// 
    /// Creates an instance of ProjectionEqualityComparer using the specified projection.
    /// 
    /// Type parameter for the keys to be compared,
    /// after being projected from the elements
    /// Projection to use when determining the key of an element
    /// A comparer which will compare elements by projecting each element to its key,
    /// and comparing keys        
    public static ProjectionEqualityComparer Create(Func projection)
    {
        return new ProjectionEqualityComparer(projection);
    }
}

/// 
/// Comparer which projects each element of the comparison to a key, and then compares
/// those keys using the specified (or default) comparer for the key type.
/// 
/// Type of elements which this comparer 
/// will be asked to compare
/// Type of the key projected
/// from the element
public class ProjectionEqualityComparer : IEqualityComparer
{
    readonly Func projection;
    readonly IEqualityComparer comparer;

    /// 
    /// Creates a new instance using the specified projection, which must not be null.
    /// The default comparer for the projected type is used.
    /// 
    /// Projection to use during comparisons
    public ProjectionEqualityComparer(Func projection)
        : this(projection, null)
    {
    }

    /// 
    /// Creates a new instance using the specified projection, which must not be null.
    /// 
    /// Projection to use during comparisons
    /// The comparer to use on the keys. May be null, in
    /// which case the default comparer will be used.
    public ProjectionEqualityComparer(Func projection, IEqualityComparer comparer)
    {
        if (projection == null)
        {
            throw new ArgumentNullException("projection");
        }
        this.comparer = comparer ?? EqualityComparer.Default;
        this.projection = projection;
    }

    /// 
    /// Compares the two specified values for equality by applying the projection
    /// to each value and then using the equality comparer on the resulting keys. Null
    /// references are never passed to the 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));
    }

    /// 
    /// Produces a hash code for the given value by projecting it and
    /// then asking the equality comparer to find the hash code of
    /// the resulting key.
    /// 
    public int GetHashCode(TSource obj)
    {
        if (obj == null)
        {
            throw new ArgumentNullException("obj");
        }
        return comparer.GetHashCode(projection(obj));
    }
}

以下是一个示例用法:

var f3 = f1.Except(f2, ProjectionEqualityComparer.Create(a => a.key));


现在唯一的问题是,为什么这种语言没有内置于语言中?
@Jon Eg.f1.Except(f2,(a,b)=> a.key.CompareTo(b.key)); 不需要预期的相等比较器,对于l.Distinct(a => a.key)等也是如此...

2> mike..:

这是一个简单的助手类,应该做你想要的

public class EqualityComparer : IEqualityComparer
{
    public EqualityComparer(Func cmp)
    {
        this.cmp = cmp;
    }
    public bool Equals(T x, T y)
    {
        return cmp(x, y);
    }

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

    public Func cmp { get; set; }
}

你可以像这样使用它:

processed.Union(suburbs, new EqualityComparer((s1, s2)
    => s1.SuburbId == s2.SuburbId));


这不起作用,因为Union和Distinct首先检查哈希代码,无论代表说什么,哈希代码都可能不同.将GetHashCode更改为始终返回0可修复问题.
@makhdumi它看起来像是一个“修复程序”,但也会通过将它们转换为O(n ^ 2)操作来破坏这些方法的性能。正确实现GetHashCode是很重要的。没有办法用这个答案使用的`cmp`函数来正确实现。这个答案和上面的评论都有危险的缺陷。@jonskeets的答案要好得多。

3> Sam Saffron..:

我发现在IEnumerable上提供额外的帮助是一种更干净的方法.

见:这个问题

所以你可以:

var f3 = f1.Except(
           f2, 
             (a, b) => a.key.CompareTo(b.key)
            );

如果正确定义扩展方法


我希望没有扩展方法我们可以做到这一点

4> 小智..:

这个项目做了类似的事情:AnonymousComparer - Linq的lambda比较选择器,它也有LINQ标准查询运算符的扩展.



5> Tamas Ionut..:

为什么不是这样的:

    public class Comparer : IEqualityComparer
    {
        private readonly Func _equalityComparer;

        public Comparer(Func equalityComparer)
        {
            _equalityComparer = equalityComparer;
        }

        public bool Equals(T first, T second)
        {
            return _equalityComparer(first, second);
        }

        public int GetHashCode(T value)
        {
            return value.GetHashCode();
        }
    }

然后你可以例如像(如在的情况下做IntersectIEnumerable):

list.Intersect(otherList, new Comparer( (x, y) => x.Property == y.Property));

Comparer类可以放在一个公共事业项目,被需要的地方使用.

我现在才看到Sam Saffron的答案(与此非常相似).

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