[翻译]如何使用LINQ方法来比较自定义类型的对象
2010-05-04 10:17
966 查看
How to use LINQ methods to compare objects of custom types http://blogs.msdn.com/csharpfaq/archive/2009/03/25/how-to-use-linq-methods-to-compare-objects-of-custom-types.aspx
在操作对象集合上,LINQ提供了一个很方便的语法和很多有用的方法。然而,要使对象被LINQ的比较方法(比如Distinct or Intersect)正确的处理,对象类型必须满足特定的要求。
让我们看看Distinct方法,distinct方法返回一个集合里面所有不同的对象。
List<int> numbers = new List<int> { 1, 1, 2, 3 };
var distinctNumbers = numbers.Distinct();
foreach (var number in distinctNumbers)
Console.WriteLine(number);
The output is:
1
2
3
如果要在自定义类型的对象集合中使用Distinct方法,我们该如何做?比如下面的例子:
class Number
{
public int Digital { get; set; }
public String Textual { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Number> numbers = new List<Number> {
new Number { Digital = 1, Textual = "one" },
new Number { Digital = 1, Textual = "one" } ,
new Number { Digital = 2, Textual = "two" } ,
new Number { Digital = 3, Textual = "three" } ,
};
var distinctNumbers = numbers.Distinct();
foreach (var number in distinctNumbers)
Console.WriteLine(number.Digital);
}
}
代码顺利通过编译,但是结果却不是我们期望的:
1
1
2
3
怎么会这样呢,答案就隐藏在LINQ的实现细节中。一个类型要想用Distinct方法,必须实现IEquatable<T> 接口并实现Equals和GetHashCode方法。
所以,之前那个例子中的Number class应该如下实现:
class Number: IEquatable<Number>
{
public int Digital { get; set; }
public String Textual { get; set; }
public bool Equals(Number other)
{
// Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
// Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
// Check whether the objects’ properties are equal.
return Digital.Equals(other.Digital) &&
Textual.Equals(other.Textual);
}
// If Equals returns true for a pair of objects,
// GetHashCode must return the same value for these objects.
public override int GetHashCode()
{
// Get the hash code for the Textual field if it is not null.
int hashTextual = Textual == null ? 0 : Textual.GetHashCode();
// Get the hash code for the Digital field.
int hashDigital = Digital.GetHashCode();
// Calculate the hash code for the object.
return hashDigital ^ hashTextual;
}
}
但是如果你的类型不能被修改,比如是在类库中(dll),这样你就不能让这个类去实现IEquatable<T>接口了。答案就是新建自己的 equality comparer类,并将comparer类的实例作为Distinct方法的参数传进去。
Equality comparer必须实现IEqualityComparer<T>接口,并提供GetHashCode和Equals方法的实现。
以下为Number类的 Equality comparer 类:
class NumberComparer : IEqualityComparer<Number>
{
public bool Equals(Number x, Number y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;
return x.Digital == y.Digital && x.Textual == y.Textual;
}
public int GetHashCode(Number number)
{
if (Object.ReferenceEquals(number, null)) return 0;
int hashTextual = number.Textual == null
? 0 : number.Textual.GetHashCode();
int hashDigital = number.Digital.GetHashCode();
return hashTextual ^ hashDigital;
}
}
不要忘记把 自定义的comparer类的实例作为Distinct的输入参数:
var distinctNumbers = numbers.Distinct(new NumberComparer());
当然,这个规则除了适用于Distinct方法,还适用于Constains,Except,Intersect和Union方法。通常地,如果你看到LINQ方法中有一个重载方法接受IEqualityComparer<T>为参数,那就说明你自定义的类型需要实现IEquatable<T> 或者创建自己的equality comparer.
其他资料(原文无此部分):
http://msdn.microsoft.com/en-us/library/ms131190.aspx
http://blogs.msdn.com/jaredpar/archive/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object-s-equals-and-gethashcode.aspx
在操作对象集合上,LINQ提供了一个很方便的语法和很多有用的方法。然而,要使对象被LINQ的比较方法(比如Distinct or Intersect)正确的处理,对象类型必须满足特定的要求。
让我们看看Distinct方法,distinct方法返回一个集合里面所有不同的对象。
List<int> numbers = new List<int> { 1, 1, 2, 3 };
var distinctNumbers = numbers.Distinct();
foreach (var number in distinctNumbers)
Console.WriteLine(number);
The output is:
1
2
3
如果要在自定义类型的对象集合中使用Distinct方法,我们该如何做?比如下面的例子:
class Number
{
public int Digital { get; set; }
public String Textual { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Number> numbers = new List<Number> {
new Number { Digital = 1, Textual = "one" },
new Number { Digital = 1, Textual = "one" } ,
new Number { Digital = 2, Textual = "two" } ,
new Number { Digital = 3, Textual = "three" } ,
};
var distinctNumbers = numbers.Distinct();
foreach (var number in distinctNumbers)
Console.WriteLine(number.Digital);
}
}
代码顺利通过编译,但是结果却不是我们期望的:
1
1
2
3
怎么会这样呢,答案就隐藏在LINQ的实现细节中。一个类型要想用Distinct方法,必须实现IEquatable<T> 接口并实现Equals和GetHashCode方法。
所以,之前那个例子中的Number class应该如下实现:
class Number: IEquatable<Number>
{
public int Digital { get; set; }
public String Textual { get; set; }
public bool Equals(Number other)
{
// Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
// Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
// Check whether the objects’ properties are equal.
return Digital.Equals(other.Digital) &&
Textual.Equals(other.Textual);
}
// If Equals returns true for a pair of objects,
// GetHashCode must return the same value for these objects.
public override int GetHashCode()
{
// Get the hash code for the Textual field if it is not null.
int hashTextual = Textual == null ? 0 : Textual.GetHashCode();
// Get the hash code for the Digital field.
int hashDigital = Digital.GetHashCode();
// Calculate the hash code for the object.
return hashDigital ^ hashTextual;
}
}
但是如果你的类型不能被修改,比如是在类库中(dll),这样你就不能让这个类去实现IEquatable<T>接口了。答案就是新建自己的 equality comparer类,并将comparer类的实例作为Distinct方法的参数传进去。
Equality comparer必须实现IEqualityComparer<T>接口,并提供GetHashCode和Equals方法的实现。
以下为Number类的 Equality comparer 类:
class NumberComparer : IEqualityComparer<Number>
{
public bool Equals(Number x, Number y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;
return x.Digital == y.Digital && x.Textual == y.Textual;
}
public int GetHashCode(Number number)
{
if (Object.ReferenceEquals(number, null)) return 0;
int hashTextual = number.Textual == null
? 0 : number.Textual.GetHashCode();
int hashDigital = number.Digital.GetHashCode();
return hashTextual ^ hashDigital;
}
}
不要忘记把 自定义的comparer类的实例作为Distinct的输入参数:
var distinctNumbers = numbers.Distinct(new NumberComparer());
当然,这个规则除了适用于Distinct方法,还适用于Constains,Except,Intersect和Union方法。通常地,如果你看到LINQ方法中有一个重载方法接受IEqualityComparer<T>为参数,那就说明你自定义的类型需要实现IEquatable<T> 或者创建自己的equality comparer.
其他资料(原文无此部分):
http://msdn.microsoft.com/en-us/library/ms131190.aspx
http://blogs.msdn.com/jaredpar/archive/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object-s-equals-and-gethashcode.aspx
相关文章推荐
- [翻译]怎么使用LINQ方法来比较自定义类型对象
- 怎么使用LINQ方法来比较自定义类型对象
- 怎么使用LINQ方法来比较自定义类型对象
- JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法
- 使用Object对象的toString()方法自定义判断数据类型方法
- 如何使用==操作符,Equals方法,ReferenceEquals方法,IEquatable接口比较2个对象
- 用LinQ扩展方法,泛型扩展方法,实现自定义验证字符是否空、对象是否为null,及泛型约束使用,Action的使用
- 如何判断对象类型/对typeof方法使用认识
- Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库 自定义日志记录功能,按日记录,很方便 C#常量和字段以及各种方法的语法总结 类型,对象,线程栈,托管堆在运行时的关系,以及clr如何调用静态方法,实例方法,和虚方法 asp.net webapi 自定义身份验证
- Integer类型数据比较大小问题:(Integer定义的是对象,养成使用equals方法的好习惯)
- 关于自定义比较函数 usort 如何使用 类中的方法
- java中的clone()方法的研究---(10)如何编写正确的clone()方法:子自定义Object类型
- python 7-6 如何使用描述符对实例属性做类型检查,分别实现set,方法,在set内使用isinstance做类型检查
- SP2010开发和VS2010专家"食谱"--第四章节—列表定义和内容类型(3)--使用对象模型创建自定义内容类型
- [转载]EF或LINQ 查询时使用IN并且根据列表自定义排序方法
- 对于返回void类型的asyc的异步方法,如何修改,能使用await
- ADO.NET Entity Framework 如何:自定义建模和映射文件以使用自定义对象(实体框架)
- 扫描识别工具Dynamic Web TWAIN使用教程:如何自定义Web TWAIN对象
- Java中使用System.out.printf().如何控制输出格式及类型不一致的解决方法
- java 如何理解泛型类(类型擦除、翻译泛型表达式,翻译泛型方法)