Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 将委托包装在IEqualityComparer中_.net_Linq_Delegates - Fatal编程技术网

.net 将委托包装在IEqualityComparer中

.net 将委托包装在IEqualityComparer中,.net,linq,delegates,.net,Linq,Delegates,几个Linq.Enumerable函数采用IEqualityComparer。是否有一个方便的包装器类可以调整委托(T,T)=>bool来实现IEqualityComparer?编写一个很容易(如果您忽略了定义正确哈希代码的问题),但我想知道是否有现成的解决方案 具体来说,我想在字典上执行set操作,只使用键定义成员身份(同时根据不同的规则保留值)。恐怕没有现成的包装器。然而,创建一个并不困难: class Comparer<T>: IEqualityComparer<T>

几个Linq.Enumerable函数采用
IEqualityComparer
。是否有一个方便的包装器类可以调整
委托(T,T)=>bool
来实现
IEqualityComparer
?编写一个很容易(如果您忽略了定义正确哈希代码的问题),但我想知道是否有现成的解决方案


具体来说,我想在
字典
上执行set操作,只使用键定义成员身份(同时根据不同的规则保留值)。

恐怕没有现成的包装器。然而,创建一个并不困难:

class Comparer<T>: IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _comparer;

    public Comparer(Func<T, T, bool> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");

        _comparer = comparer;
    }

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

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

...

Func<int, int, bool> f = (x, y) => x == y;
var comparer = new Comparer<int>(f);
Console.WriteLine(comparer.Equals(1, 1));
Console.WriteLine(comparer.Equals(1, 2));
类比较器:IEqualityComparer
{
专用只读函数比较器;
公共比较器(函数比较器)
{
if(比较器==null)
抛出新的ArgumentNullException(“comparer”);
_比较器=比较器;
}
公共布尔等于(TX,TY)
{
返回比较器(x,y);
}
公共int GetHashCode(T obj)
{
返回obj.ToString().ToLower().GetHashCode();
}
}
...
Func f=(x,y)=>x==y;
var比较器=新比较器(f);
Console.WriteLine(comparer.Equals(1,1));
Console.WriteLine(comparer.Equals(1,2));

我不知道现有的类,但类似于:

public class MyComparer<T> : IEqualityComparer<T>
{
  private Func<T, T, bool> _compare;
  MyComparer(Func<T, T, bool> compare)
  {
    _compare = compare;
  }

  public bool Equals(T x, Ty)
  {
    return _compare(x, y);
  }

  public int GetHashCode(T obj)
  {
    return obj.GetHashCode();
  }
}
公共类MyComparer:IEqualityComparer
{
私有函数比较;
MyComparer(功能比较)
{
_比较=比较;
}
公共布尔等于(tx,Ty)
{
返回_比较(x,y);
}
公共int GetHashCode(T obj)
{
返回obj.GetHashCode();
}
}

注意:我还没有编译并运行这个程序,所以可能有输入错误或其他错误。

我将回答我自己的问题。要将字典视为集合,最简单的方法似乎是将集合操作应用于dict.Keys,然后将其转换回具有Enumerable.ToDictionary(…)的字典。

公共类FunctionEqualityComparer:IEqualityComparer
{
只读函数比较器;
只读函数散列;
公共函数相等比较程序(函数比较程序)
:这(比较器,t=>t.GetHashCode())
{
}
公共FuncEqualityComparer(Func比较器、Func哈希)
{
_比较器=比较器;
_散列=散列;
}
公共布尔等于(TX,TY)
{
返回比较器(x,y);
}
公共int GetHashCode(T obj)
{
返回散列(obj);
}
}
带扩展名:-

public static class SequenceExtensions
{
    public static bool SequenceEqual<T>( this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer )
    {
        return first.SequenceEqual( second, new FuncEqualityComparer<T>( comparer ) );
    }

    public static bool SequenceEqual<T>( this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer, Func<T, int> hash )
    {
        return first.SequenceEqual( second, new FuncEqualityComparer<T>( comparer, hash ) );
    }
}
公共静态类SequenceExtensions
{
公共静态布尔序列相等(此IEnumerable first,IEnumerable second,Func comparer)
{
返回first.SequenceEqual(第二个,新的functionequalitycomparer(comparer));
}
public static bool SequenceEqual(此IEnumerable first,IEnumerable second,Func comparer,Func hash)
{
返回first.SequenceEqual(第二个,新的functequalitycomparer(comparer,hash));
}
}

当您想要自定义相等性检查时,99%的时间您感兴趣的是定义要比较的键,而不是比较本身

这可能是一个优雅的解决方案(来自Python的概念)

用法:

var foo = new List<string> { "abc", "de", "DE" };

// case-insensitive distinct
var distinct = foo.Distinct(new KeyEqualityComparer<string>( x => x.ToLower() ) );
var n = ItemList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList();
n.AddRange(OtherList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList(););
n = n.Distinct(x=>new{Vchr=x.Vchr,Id=x.Id}).ToList();
只有一个优化: 我们可以使用开箱即用的EqualityComparer进行值比较,而不是委派它

这也会使实现更干净,因为实际的比较逻辑现在保留在GetHashCode()和Equals()中,您可能已经重载了它们

代码如下:

public class MyComparer<T> : IEqualityComparer<T> 
{ 
  public bool Equals(T x, T y) 
  { 
    return EqualityComparer<T>.Default.Equals(x, y); 
  } 

  public int GetHashCode(T obj) 
  { 
    return obj.GetHashCode(); 
  } 
} 
公共类MyComparer:IEqualityComparer
{ 
公共布尔等于(TX,TY)
{ 
返回EqualityComparer.Default.Equals(x,y);
} 
公共int GetHashCode(T obj)
{ 
返回obj.GetHashCode();
} 
} 
不要忘记在对象上重载GetHashCode()和Equals()方法

这篇文章帮助了我:


Sushil

通常,我会通过在答案上评论@Sam来解决这个问题(我对原始帖子做了一些编辑,在不改变行为的情况下将其清理干净。)

以下是我的riff of,对默认哈希策略进行了[IMNSHO]关键修复:-

class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    readonly Func<T, T, bool> _comparer;
    readonly Func<T, int> _hash;

    public FuncEqualityComparer( Func<T, T, bool> comparer )
        : this( comparer, t => 0 ) // NB Cannot assume anything about how e.g., t.GetHashCode() interacts with the comparer's behavior
    {
    }

    public FuncEqualityComparer( Func<T, T, bool> comparer, Func<T, int> hash )
    {
        _comparer = comparer;
        _hash = hash;
    }

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

    public int GetHashCode( T obj )
    {
        return _hash( obj );
    }
}
类FuncEqualityComparer:IEqualityComparer
{
只读函数比较器;
只读函数散列;
公共函数相等比较程序(函数比较程序)
:this(comparer,t=>0)//NB不能假设关于例如t.GetHashCode()如何与comparer的行为交互的任何内容
{
}
公共FuncEqualityComparer(Func比较器、Func哈希)
{
_比较器=比较器;
_散列=散列;
}
公共布尔等于(TX,TY)
{
返回比较器(x,y);
}
公共int GetHashCode(T obj)
{
返回散列(obj);
}
}
关于
GetHashCode
其他人已经评论过这样一个事实,即任何定制的
IEqualityComparer
实现都应该包含
GetHashCode
方法;但没有人愿意详细解释原因

原因如下。您的问题特别提到LINQ扩展方法;几乎所有这些都依赖于哈希代码来正常工作,因为它们在内部利用哈希表来提高效率

比如说。如果这个扩展方法使用的是<代码>等于方法,则考虑该扩展方法的含义。如果只有
等于
,如何确定项目是否已按顺序扫描?枚举已查看的整个值集合,并检查是否匹配。这将导致使用最坏情况下的O(N2)算法而不是O(N)算法进行
Distinct

幸运的是,情况并非如此
Distinct
不只是使用
等于
;它还使用
GetHashCode
。事实上,如果没有提供适当的
GetHashCode
IEqualityComparer
,它绝对无法正常工作。下面是一个人为的例子来说明这一点

假设我有以下类型:

class Value
{
    public string Name { get; private set; }
    public int Number { get; private set; }

    public Value(string name, int number)
    {
        Name = name;
        Number = number;
    }

    public override string ToString()
    {
        return string.Format("{0}: {1}", Name, Number);
    }
}
现在说我有
class Value
{
    public string Name { get; private set; }
    public int Number { get; private set; }

    public Value(string name, int number)
    {
        Name = name;
        Number = number;
    }

    public override string ToString()
    {
        return string.Format("{0}: {1}", Name, Number);
    }
}
var comparer = new Comparer<Value>((x, y) => x.Name == y.Name);
var values = new List<Value>();

var random = new Random();
for (int i = 0; i < 10; ++i)
{
    values.Add("x", random.Next());
}

var distinct = values.Distinct(comparer);

foreach (Value x in distinct)
{
    Console.WriteLine(x);
}
x: 1346013431 x: 1388845717 x: 1576754134 x: 1104067189 x: 1144789201 x: 1862076501 x: 1573781440 x: 646797592 x: 655632802 x: 1206819377
var grouped = values.GroupBy(x => x, comparer);

foreach (IGrouping<Value> g in grouped)
{
    Console.WriteLine("[KEY: '{0}']", g);
    foreach (Value x in g)
    {
        Console.WriteLine(x);
    }
}
[KEY = 'x: 1346013431'] x: 1346013431 [KEY = 'x: 1388845717'] x: 1388845717 [KEY = 'x: 1576754134'] x: 1576754134 [KEY = 'x: 1104067189'] x: 1104067189 [KEY = 'x: 1144789201'] x: 1144789201 [KEY = 'x: 1862076501'] x: 1862076501 [KEY = 'x: 1573781440'] x: 1573781440 [KEY = 'x: 646797592'] x: 646797592 [KEY = 'x: 655632802'] x: 655632802 [KEY = 'x: 1206819377'] x: 1206819377
var uniqueValues = new HashSet<Value>(values, comparer);

foreach (Value x in uniqueValues)
{
    Console.WriteLine(x);
}
x: 1346013431 x: 1388845717 x: 1576754134 x: 1104067189 x: 1144789201 x: 1862076501 x: 1573781440 x: 646797592 x: 655632802 x: 1206819377
public class KeyEqualityComparer<T, TKey> : IEqualityComparer<T>
{
    protected readonly Func<T, TKey> keyExtractor;

    public KeyEqualityComparer(Func<T, TKey> keyExtractor)
    {
        this.keyExtractor = keyExtractor;
    }

    public virtual bool Equals(T x, T y)
    {
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }

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

public class StrictKeyEqualityComparer<T, TKey> : KeyEqualityComparer<T, TKey>
    where TKey : IEquatable<TKey>
{
    public StrictKeyEqualityComparer(Func<T, TKey> keyExtractor)
        : base(keyExtractor)
    { }

    public override bool Equals(T x, T y)
    {
        // This will use the overload that accepts a TKey parameter
        // instead of an object parameter.
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }
}
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, object>    keyExtractor)
{
    return list.Distinct(new KeyEqualityComparer<T>(keyExtractor));
}
var distinct = foo.Distinct(x => x.ToLower())
public static Dictionary<TKey, TValue> Distinct<TKey, TValue>(this IEnumerable<TValue> items, Func<TValue, TKey> selector)
  {
     Dictionary<TKey, TValue> result = null;
     ICollection collection = items as ICollection;
     if (collection != null)
        result = new Dictionary<TKey, TValue>(collection.Count);
     else
        result = new Dictionary<TKey, TValue>();
     foreach (TValue item in items)
        result[selector(item)] = item;
     return result;
  }
Equality<Person>.CreateComparer(p => p.ID);
new EqualityComparer<Person, int>(p => p.ID);
public static class Equality<T>
{
    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector)
    {
        return CreateComparer(keySelector, null);
    }

    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector, 
                                                         IEqualityComparer<V> comparer)
    {
        return new KeyEqualityComparer<V>(keySelector, comparer);
    }

    class KeyEqualityComparer<V> : IEqualityComparer<T>
    {
        readonly Func<T, V> keySelector;
        readonly IEqualityComparer<V> comparer;

        public KeyEqualityComparer(Func<T, V> keySelector, 
                                   IEqualityComparer<V> comparer)
        {
            if (keySelector == null)
                throw new ArgumentNullException("keySelector");

            this.keySelector = keySelector;
            this.comparer = comparer ?? EqualityComparer<V>.Default;
        }

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

        public int GetHashCode(T obj)
        {
            return comparer.GetHashCode(keySelector(obj));
        }
    }
}
var comparer1 = Equality<Person>.CreateComparer(p => p.ID);
var comparer2 = Equality<Person>.CreateComparer(p => p.Name);
var comparer3 = Equality<Person>.CreateComparer(p => p.Birthday.Year);
var comparer4 = Equality<Person>.CreateComparer(p => p.Name, StringComparer.CurrentCultureIgnoreCase);
class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
}
    public static class Comparer 
    {
      public static IEqualityComparer<T> CreateComparerForElements<T>(this IEnumerable<T> enumerable, Func<T, object> keyExtractor)
      {
        return new KeyEqualityComparer<T>(keyExtractor);
      }
    }
var n = ItemList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList();
n.AddRange(OtherList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList(););
n = n.Distinct(x=>new{Vchr=x.Vchr,Id=x.Id}).ToList();
persons1.Union(persons2, person => person.LastName)
public class LambdaEqualityComparer<TSource, TComparable> : IEqualityComparer<TSource>
{
  Func<TSource, TComparable> _keyGetter;

  public LambdaEqualityComparer(Func<TSource, TComparable> keyGetter)
  {
    _keyGetter = keyGetter;
  }

  public bool Equals(TSource x, TSource y)
  {
    if (x == null || y == null) return (x == null && y == null);
    return object.Equals(_keyGetter(x), _keyGetter(y));
  }

  public int GetHashCode(TSource obj)
  {
    if (obj == null) return int.MinValue;
    var k = _keyGetter(obj);
    if (k == null) return int.MaxValue;
    return k.GetHashCode();
  }
}
public static class LambdaEqualityComparer
{
       // source1.Union(source2, lambda)
        public static IEnumerable<TSource> Union<TSource, TComparable>(
           this IEnumerable<TSource> source1, 
           IEnumerable<TSource> source2, 
            Func<TSource, TComparable> keySelector)
        {
            return source1.Union(source2, 
               new LambdaEqualityComparer<TSource, TComparable>(keySelector));
       }
   }