HashSet<object> objs = new HashSet<object>(); objs.Add(new { a = 1, b = 2 }); // true objs.Add(new { a = 1, b = 2 }); // false objs.Add(new Man { id = 1, name = "A" }); // true objs.Add(new Man { id = 1, name = "A" }); // true Console.WriteLine(new { a = 1, b = 2 } == new { a = 1, b = 2 }); // false Console.WriteLine(new Man { id = 1, name = "A" } == new Man { id = 1, name = "A" }); // false Console.WriteLine(object.Equals(new { a = 1, b = 2 }, new { a = 1, b = 2 })); // true Console.WriteLine(object.Equals(new Man { id = 1, name = "A" }, new Man { id = 1, name = "A" })); // false Console.WriteLine(object.ReferenceEquals(new { a = 1, b = 2 }, new { a = 1, b = 2 })); // false Console.WriteLine(object.ReferenceEquals(new Man { id = 1, name = "A" }, new Man { id = 1, name = "A" })); // false Console.WriteLine(new { a = 1, b = 2}.GetHashCode()); Console.WriteLine(new { a = 1, b = 2}.GetHashCode()); // 内容相同的,HashCode都相同 Console.WriteLine(new { a = 1, b = 3}.GetHashCode()); Console.WriteLine(new Man { id = 1, name = "A" }.GetHashCode()); // 每次都不同 Console.WriteLine(new Man { id = 1, name = "A" }.GetHashCode()); // 每次都不同 // 结论: // 匿名对象的 GetHashCode 和 Equals 是相同的,以便于方便地用作LINQ连接和分组中的散列键,这样的设计是合理的; // HashSet.Add 方法判断对象是否已经存在,是根据 GetHashCode 和 Equals 进行的,单纯的 HashCode 相同没用,还是会添加; // 所以,下面的 DistinctBy 扩展方法 .DistinctBy(p => new { p.Id, p.Name }) 或 .DistinctBy(p => p.Id) 都可以正常工作; // 参考[https://stackoverflow.com/questions/12123512/why-does-the-equals-implementation-for-anonymous-types-compare-fields] // There is also a very practical reason to do this: Anonymous types are convenient to use as hash keys in LINQ joins and groupings. public class Man { public int id { get; set; } public string name { get; set; } //public override int GetHashCode() //{ // return 123; //} //public override bool Equals(object obj) //{ // return ((Man)obj).id == this.id && ((Man)obj).name == this.name; //} } public static class Extensions { public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { HashSet<TKey> seenKeys = new HashSet<TKey>(); foreach (TSource element in source) { if (seenKeys.Add(keySelector(element))) { yield return element; } } } }