C# } 公共int GetHashCode(T obj) { if(obj==null) 返回0; var散列=\u属性 .Select(pi=>pi.GetValue(obj)?.GetHashCode()???0.ToArray(); 返回联合收割机(散列
} 公共int GetHashCode(T obj) { if(obj==null) 返回0; var散列=\u属性 .Select(pi=>pi.GetValue(obj)?.GetHashCode()???0.ToArray(); 返回联合收割机(散列); } 静态int组合(int[]散列) { int结果=0; foreach(散列中的var散列) { uint rol5=((uint)结果>27); 结果=((int)rol5+result)^hash; } 返回结果; } 静态属性Info ExtractProperty(表达式属性) { if(property.NodeType!=ExpressionType.Lambda) throwEx(); var body=property.body; if(body.NodeType==ExpressionType.Convert) if(body是一元表达式) body=一元操作数; 其他的 throwEx(); 如果(!(正文是MemberExpression成员)) throwEx(); 如果(!(member.member是PropertyInfo pi)) throwEx(); 返回pi; void throwEx()=> 抛出新的NotSupportedException($“表达式{property}不受支持。”); } }C# } 公共int GetHashCode(T obj) { if(obj==null) 返回0; var散列=\u属性 .Select(pi=>pi.GetValue(obj)?.GetHashCode()???0.ToArray(); 返回联合收割机(散列,c#,linq,distinct,C#,Linq,Distinct,} 公共int GetHashCode(T obj) { if(obj==null) 返回0; var散列=\u属性 .Select(pi=>pi.GetValue(obj)?.GetHashCode()???0.ToArray(); 返回联合收割机(散列); } 静态int组合(int[]散列) { int结果=0; foreach(散列中的var散列) { uint rol5=((uint)结果>27); 结果=((int)rol5+result)^hash; } 返回结果; } 静态属性I
是的,我只是通过ToLookup()来查看。可能效率低,速度慢,但可以完成此任务。在上面/下面发布我的声明我同意框架似乎缺少一些东西。我不知道这是不是一个合格的比较。。。这两种方法都需要。感觉应该有一种更简单的方法来使用Distinct:一种接受谓词的覆盖,而不是谓词。我的意思是对Distinct的重写,它将取T,让你选择你想要用于区分的对象。@charlie-对,这就是我实际上认为我将使用现有Distinct(..)得到的结果。我以前从未在这种情况下使用过它,当然,它并不是我所期望的那么好。然后你也可以实现我所希望的对Distinct的覆盖。是的,你可以很容易地做到这一点并得到你想要的。但是你不是还在实现IEqualityComparer吗。听起来好像你不想那样做。注意,这不一定行得通;不能保证您提供的GetHashCode实现与Equals方法一致。这可能会给出错误的结果。对于IEnumerable.Distinct等方法来说,在执行可能更昂贵的相等性检查之前,使用GetHashCode()函数对项目进行装箱是完全合理的。您的实现不符合IEqualityComparer的约定。忽略
TSource
参数的第二个静态创建方法有什么意义?公共静态ProjectionQualityComparer创建(TSource被忽略,Func投影){返回新的ProjectionQualityComparer(投影);}@血肉:当您可能无法显式指定类型时,它允许类型推断启动-例如,对于匿名类型。答案中的源代码中缺少ThrowIfNull,因此不会编译。这似乎是可行的:publicstatict ThrowIfNull(这个T值,字符串variableName),其中T:class{if(value==null){throw new NullReferenceException(string.Format(“value is null:{0}”,variableName));}返回值;}
@Daryl:Yes,差不多就是这样-除了它应该抛出ArgumentNullException
。将添加MiscUtil中的版本。。。
class GalleryImage {
public int Key { get;set; }
public string Caption { get;set; }
public string Filename { get; set; }
public string[] Tags {g et; set; }
}
var allImages = Galleries.SelectMany(x => x.Images);
var distinctImages = allImages.Distinct<GalleryImage>(new
EqualityComparer<GalleryImage>((a, b) => a.id == b.id));
Galleries.SelectMany(x => x.Images).ToLookup(x => x.id).Select(x => x.First());
public class ThrowAwayEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> comparer;
public ThrowAwayEqualityComparer(Func<T, T, bool> comparer)
{
this.comparer = comparer;
}
public bool Equals(T a, T b)
{
return comparer(a, b);
}
public int GetHashCode(T a)
{
return a.GetHashCode();
}
}
var distinctImages = allImages.Distinct(
new ThrowAwayEqualityComparer<GalleryImage>((a, b) => a.Key == b.Key));
public static class IEnumerableExtensions
{
public static IEnumerable<TValue> Distinct<TValue>(this IEnumerable<TValue> @this, Func<TValue, TValue, bool> comparer)
{
return @this.Distinct(new ThrowAwayEqualityComparer<TValue>(comparer);
}
private class ThrowAwayEqualityComparer...
}
public static IEnumerable<T> Distinct<T, U>(
this IEnumerable<T> seq, Func<T, U> getKey)
{
return
from item in seq
group item by getKey(item) into gp
select gp.First();
}
public class KeyEqualityComparer<T,U> : IEqualityComparer<T>
{
private Func<T,U> GetKey { get; set; }
public KeyEqualityComparer(Func<T,U> 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();
}
}
EqualityComparer<GalleryImage> comparer =
ProjectionEqualityComparer<GalleryImage>.Create(x => x.id);
// Helper class for construction
public static class ProjectionEqualityComparer
{
public static ProjectionEqualityComparer<TSource, TKey>
Create<TSource, TKey>(Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
public static ProjectionEqualityComparer<TSource, TKey>
Create<TSource, TKey> (TSource ignored,
Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
}
public static class ProjectionEqualityComparer<TSource>
{
public static ProjectionEqualityComparer<TSource, TKey>
Create<TKey>(Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
}
public class ProjectionEqualityComparer<TSource, TKey>
: IEqualityComparer<TSource>
{
readonly Func<TSource, TKey> projection;
readonly IEqualityComparer<TKey> comparer;
public ProjectionEqualityComparer(Func<TSource, TKey> projection)
: this(projection, null)
{
}
public ProjectionEqualityComparer(
Func<TSource, TKey> projection,
IEqualityComparer<TKey> comparer)
{
projection.ThrowIfNull("projection");
this.comparer = comparer ?? EqualityComparer<TKey>.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));
}
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
return source.DistinctBy(keySelector, null);
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
source.ThrowIfNull("source");
keySelector.ThrowIfNull("keySelector");
return DistinctByImpl(source, keySelector, comparer);
}
private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>
(IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
HashSet<TKey> knownKeys = new HashSet<TKey>(comparer);
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
public static void ThrowIfNull<T>(this T data, string name) where T : class
{
if (data == null)
{
throw new ArgumentNullException(name);
}
}
IEqualityComparer<Contact> comp1 = EqualityComparerImpl<Contact>.Create(c => c.Name);
var comp2 = EqualityComparerImpl<Contact>.Create(c => c.Name, c => c.Age);
class Contact { public Name { get; set; } public Age { get; set; } }
public class EqualityComparerImpl<T> : IEqualityComparer<T>
{
public static EqualityComparerImpl<T> Create(
params Expression<Func<T, object>>[] properties) =>
new EqualityComparerImpl<T>(properties);
PropertyInfo[] _properties;
EqualityComparerImpl(Expression<Func<T, object>>[] properties)
{
if (properties == null)
throw new ArgumentNullException(nameof(properties));
if (properties.Length == 0)
throw new ArgumentOutOfRangeException(nameof(properties));
var length = properties.Length;
var extractions = new PropertyInfo[length];
for (int i = 0; i < length; i++)
{
var property = properties[i];
extractions[i] = ExtractProperty(property);
}
_properties = extractions;
}
public bool Equals(T x, T y)
{
if (ReferenceEquals(x, y))
//covers both are null
return true;
if (x == null || y == null)
return false;
var len = _properties.Length;
for (int i = 0; i < _properties.Length; i++)
{
var property = _properties[i];
if (!Equals(property.GetValue(x), property.GetValue(y)))
return false;
}
return true;
}
public int GetHashCode(T obj)
{
if (obj == null)
return 0;
var hashes = _properties
.Select(pi => pi.GetValue(obj)?.GetHashCode() ?? 0).ToArray();
return Combine(hashes);
}
static int Combine(int[] hashes)
{
int result = 0;
foreach (var hash in hashes)
{
uint rol5 = ((uint)result << 5) | ((uint)result >> 27);
result = ((int)rol5 + result) ^ hash;
}
return result;
}
static PropertyInfo ExtractProperty(Expression<Func<T, object>> property)
{
if (property.NodeType != ExpressionType.Lambda)
throwEx();
var body = property.Body;
if (body.NodeType == ExpressionType.Convert)
if (body is UnaryExpression unary)
body = unary.Operand;
else
throwEx();
if (!(body is MemberExpression member))
throwEx();
if (!(member.Member is PropertyInfo pi))
throwEx();
return pi;
void throwEx() =>
throw new NotSupportedException($"The expression '{property}' isn't supported.");
}
}