我已经开发了一个通用的PropertyEqualityComparer工作正常,但我不确定我是否以正确的方式完成它,所以如果有些人可以改进代码或评论家,他是受欢迎的.
注意:如果是Reference类型,属性应该实现IEquatable.
public sealed class PropertyEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Type _iequatable = Type.GetType("System.IEquatable`1", false, true);
private readonly PropertyInfo _property;
public PropertyEqualityComparer(string property)
{
PropertyInfo propInfos = typeof(T).GetProperty(property);
if (propInfos == null)
{
throw new ArgumentNullException();
}
// Ensure Property is Equatable (override of HashCode)
if (propInfos.PropertyType.IsValueType
|| (!propInfos.PropertyType.IsValueType
&& propInfos.PropertyType.GetInterfaces().Any(type => type.Name == _iequatable.Name)))
{
_property = propInfos;
}
else
{
throw new ArgumentException();
}
}
public bool Equals(T x, T y)
{
var xValue = _property.GetValue(x, null);
var yValue = _property.GetValue(y, null);
return xValue.Equals(yValue);
}
public int GetHashCode(T obj)
{
return _property.GetValue(obj, null).GetHashCode();
}
}
Run Code Online (Sandbox Code Playgroud)
我创建了两个类来查看是否有效,一切都很好,这里是类:
public sealed class A
{
private string _s1;
private B _b;
public A(string s1, B b)
{
_s1 = s1;
_b = b;
}
public string S1
{
get { return _s1; }
set { _s1 = value; }
}
public B B
{
get { return _b; }
set { _b = value; }
}
}
public sealed class B : IEquatable<B>
{
private string _s;
public string S
{
get { return _s; }
set { _s = value; }
}
public B(string s)
{
S = s;
}
public override int GetHashCode()
{
return S.GetHashCode();
}
public override bool Equals(object obj)
{
return Equals(obj as B);
}
public bool Equals(B other)
{
return (other == null)
? false
: this.S == other.S;
}
}
Run Code Online (Sandbox Code Playgroud)
和测试代码:
B b = new B("baby");
A[] __a = { new A("first", b), new A("second", b), new A("third", b)};
PropertyEqualityComparer<A> aComparer = new PropertyEqualityComparer<A>("B");
var vDistinct = __a.Distinct(aComparer).ToArray();
// vDistinct = __a[0] { first, baby }
var vContains = __a.Contains(new A("a", new B("baby")), aComparer);
// True
vContains = __a.Contains(new A("b", new B("foobar")), aComparer);
// False
Run Code Online (Sandbox Code Playgroud)
这里有待改进吗?
谢谢 !
由于T必须在编译时知道类型,因此不需要将属性名称作为a传递string.您可以传递委托来执行类型安全的快速成员访问,而不是脆弱的慢反射:
var comparer = new ProjectionEqualityComparer<A, B>(a => a.B);
// ...
public sealed class ProjectionEqualityComparer<TSource, TKey>
: EqualityComparer<TSource>
{
private readonly Func<TSource, TKey> _keySelector;
private readonly IEqualityComparer<TKey> _keyComparer;
public ProjectionEqualityComparer(Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer = null)
{
if (keySelector == null)
throw new ArgumentNullException("keySelector");
_keySelector = keySelector;
_keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
}
public override bool Equals(TSource x, TSource y)
{
if (x == null)
return (y == null);
if (y == null)
return false;
return _keyComparer.Equals(_keySelector(x), _keySelector(y));
}
public override int GetHashCode(TSource obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
return _keyComparer.GetHashCode(_keySelector(obj));
}
}
Run Code Online (Sandbox Code Playgroud)
如果你真的需要强制执行IEquatable<T>规则,那么你可以添加一个where TKey : IEquatable<TKey>通用约束,但它确实不是必需的.使用时EqualityComparer<TKey>.Default应该照顾好所有这些,或者IEqualityComparer<TKey>如果您愿意,可以传递自定义实现.
| 归档时间: |
|
| 查看次数: |
184 次 |
| 最近记录: |