명시 적 유형 비교기를 인라인으로 지정할 수 있습니까?
따라서 .NET 3.0 / 3.5는 LINQ와 함께 제공되는 모든 깔끔한 함수 덕분에 데이터를 쿼리, 정렬 및 조작하는 많은 새로운 방법을 제공합니다. 때로는 내장 된 비교 연산자가없는 사용자 정의 유형을 비교해야합니다. 대부분의 경우 비교는 정말 간단합니다. foo1.key? = foo2.key와 같습니다. 형식에 대한 새 IEqualityComparer를 만드는 대신 익명 대리자 / 람다 함수를 사용하여 간단히 비교 인라인을 지정할 수 있습니까? 다음과 같은 것 :
var f1 = ...,
f2 = ...;
var f3 = f1.Except(
f2, new IEqualityComparer(
(Foo a, Foo b) => a.key.CompareTo(b.key)
) );
위의 내용이 실제로 작동하지 않는다고 확신합니다. 프로그램에 사과와 사과를 비교하는 방법을 알려주기 위해 전체 수업에서 "무거운"무언가를 만들고 싶지는 않습니다.
내 MiscUtil 라이브러리에는 프로젝션 델리게이트에서 IComparer <T>를 빌드하는 ProjectionComparer가 포함되어 있습니다. ProjectionEqualityComparer가 같은 일을하도록 만드는 것은 10 분의 작업입니다.
편집 : ProjectionEqualityComparer에 대한 코드는 다음과 같습니다.
using System;
using System.Collections.Generic;
/// <summary>
/// Non-generic class to produce instances of the generic class,
/// optionally using type inference.
/// </summary>
public static class ProjectionEqualityComparer
{
/// <summary>
/// Creates an instance of ProjectionEqualityComparer using the specified projection.
/// </summary>
/// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam>
/// <typeparam name="TKey">Type parameter for the keys to be compared,
/// after being projected from the elements</typeparam>
/// <param name="projection">Projection to use when determining the key of an element</param>
/// <returns>A comparer which will compare elements by projecting
/// each element to its key, and comparing keys</returns>
public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey>(Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
/// <summary>
/// Creates an instance of ProjectionEqualityComparer using the specified projection.
/// The ignored parameter is solely present to aid type inference.
/// </summary>
/// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam>
/// <typeparam name="TKey">Type parameter for the keys to be compared,
/// after being projected from the elements</typeparam>
/// <param name="ignored">Value is ignored - type may be used by type inference</param>
/// <param name="projection">Projection to use when determining the key of an element</param>
/// <returns>A comparer which will compare elements by projecting
/// each element to its key, and comparing keys</returns>
public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey>
(TSource ignored,
Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
}
/// <summary>
/// Class generic in the source only to produce instances of the
/// doubly generic class, optionally using type inference.
/// </summary>
public static class ProjectionEqualityComparer<TSource>
{
/// <summary>
/// Creates an instance of ProjectionEqualityComparer using the specified projection.
/// </summary>
/// <typeparam name="TKey">Type parameter for the keys to be compared,
/// after being projected from the elements</typeparam>
/// <param name="projection">Projection to use when determining the key of an element</param>
/// <returns>A comparer which will compare elements by projecting each element to its key,
/// and comparing keys</returns>
public static ProjectionEqualityComparer<TSource, TKey> Create<TKey>(Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TSource">Type of elements which this comparer
/// will be asked to compare</typeparam>
/// <typeparam name="TKey">Type of the key projected
/// from the element</typeparam>
public class ProjectionEqualityComparer<TSource, TKey> : IEqualityComparer<TSource>
{
readonly Func<TSource, TKey> projection;
readonly IEqualityComparer<TKey> comparer;
/// <summary>
/// Creates a new instance using the specified projection, which must not be null.
/// The default comparer for the projected type is used.
/// </summary>
/// <param name="projection">Projection to use during comparisons</param>
public ProjectionEqualityComparer(Func<TSource, TKey> projection)
: this(projection, null)
{
}
/// <summary>
/// Creates a new instance using the specified projection, which must not be null.
/// </summary>
/// <param name="projection">Projection to use during comparisons</param>
/// <param name="comparer">The comparer to use on the keys. May be null, in
/// which case the default comparer will be used.</param>
public ProjectionEqualityComparer(Func<TSource, TKey> projection, IEqualityComparer<TKey> comparer)
{
if (projection == null)
{
throw new ArgumentNullException("projection");
}
this.comparer = comparer ?? EqualityComparer<TKey>.Default;
this.projection = projection;
}
/// <summary>
/// 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.
/// </summary>
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));
}
/// <summary>
/// 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.
/// </summary>
public int GetHashCode(TSource obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
return comparer.GetHashCode(projection(obj));
}
}
다음은 샘플 사용입니다.
var f3 = f1.Except(f2, ProjectionEqualityComparer<Foo>.Create(a => a.key));
여기 당신이 원하는 것을해야하는 간단한 도우미 클래스가 있습니다.
public class EqualityComparer<T> : IEqualityComparer<T>
{
public EqualityComparer(Func<T, T, bool> 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<T, T, bool> cmp { get; set; }
}
다음과 같이 사용할 수 있습니다.
processed.Union(suburbs, new EqualityComparer<Suburb>((s1, s2)
=> s1.SuburbId == s2.SuburbId));
IEnumerable에 추가 도우미를 제공하는 것이이를 수행하는 더 깨끗한 방법이라는 것을 알았습니다.
참조 : 이 질문
따라서 다음과 같이 할 수 있습니다.
var f3 = f1.Except(
f2,
(a, b) => a.key.CompareTo(b.key)
);
확장 방법을 올바르게 정의한 경우
이 프로젝트는 비슷한 작업을 수행합니다. AnonymousComparer-Linq 에 대한 람다 비교 선택기 , LINQ 표준 쿼리 연산자에 대한 확장도 포함되어 있습니다.
다음과 같은 이유는 무엇입니까?
public class Comparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _equalityComparer;
public Comparer(Func<T, T, bool> equalityComparer)
{
_equalityComparer = equalityComparer;
}
public bool Equals(T first, T second)
{
return _equalityComparer(first, second);
}
public int GetHashCode(T value)
{
return value.GetHashCode();
}
}
and then you could do for instance something like (e.g. in the case of Intersect
in IEnumerable<T>
):
list.Intersect(otherList, new Comparer<T>( (x, y) => x.Property == y.Property));
The Comparer
class can be put in a utilities project and used wherever is needed.
I only now see the Sam Saffron's answer (which is very similar to this one).
For small sets, you can do:
f3 = f1.Where(x1 => f2.All(x2 => x2.key != x1.key));
For large sets, you will want something more efficient in the search like:
var tmp = new HashSet<string>(f2.Select(f => f.key));
f3 = f1.Where(f => tmp.Add(f.key));
But, here, the Type
of key must implement IEqualityComparer
(above I assumed it was a string
). So, this doesn't really answer your question about using a lambda in this situation but it does use less code then some of the answers that do.
You might rely on the optimizer and shorten the second solution to:
f3 = f1.Where(x1 => (new HashSet<string>(f2.Select(x2 => x2.key))).Add(x1.key));
but, I haven't run tests to know if it runs at the same speed. And that one liner might be too clever to maintain.
Building on other answers the creation of a generic comparer was the one I liked most. But I got a problem with Linq Enumerable.Union
(msdn .Net reference) which was that its using the GetHashCode directly without taking into account the Equals override.
That took me to implement the Comparer as:
public class Comparer<T> : IEqualityComparer<T>
{
private readonly Func<T, int> _hashFunction;
public Comparer(Func<T, int> hashFunction)
{
_hashFunction = hashFunction;
}
public bool Equals(T first, T second)
{
return _hashFunction(first) == _hashFunction(second);
}
public int GetHashCode(T value)
{
return _hashFunction(value);
}
}
Using it like this:
list.Union(otherList, new Comparer<T>( x => x.StringValue.GetHashCode()));
Note that comparison might give some false positive since information being compared is mapped to an int
value.
Like the other answers but more concise c# 7:
public class LambdaComparer<T> : IEqualityComparer<T> {
private readonly Func<T, T, bool> lambdaComparer;
private readonly Func<T, int> lambdaHash;
public LambdaComparer(Func<T, T, bool> lambdaComparer) : this(lambdaComparer, o => o.GetHashCode()) {}
public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash) { this.lambdaComparer = lambdaComparer; this.lambdaHash = lambdaHash; }
public bool Equals(T x, T y) => lambdaComparer is null ? false : lambdaComparer(x, y);
public int GetHashCode(T obj) => lambdaHash is null ? 0 : lambdaHash(obj);
}
then:
var a=List<string> { "a", "b" };
var b=List<string> { "a", "*" };
return a.SequenceEquals(b, new LambdaComparer<string>((s1, s2) => s1 is null ? s2 is null : s1 == s2 || s2 == "*");
ReferenceURL : https://stackoverflow.com/questions/188120/can-i-specify-my-explicit-type-comparator-inline
'program story' 카테고리의 다른 글
기기 대기 중 오류 : 에뮬레이터가 온라인 상태가되기를 기다리는 300 초 후 시간 초과 (0) | 2021.01.07 |
---|---|
모든 정수 값이 double로 완벽하게 표현됩니까? (0) | 2021.01.07 |
분기 삭제 (0) | 2021.01.07 |
JCheckBox가 확인되었는지 확인하는 방법은 무엇입니까? (0) | 2021.01.07 |
Scala의 증분 (++) 연산자 (0) | 2021.01.07 |