2012-08-25 22 views
43

Cũng giống như trong câu hỏi, tôi chỉ tự hỏi tại sao các nhà thiết kế ngôn ngữ lại muốn thực hiện Equals trên các kiểu vô danh hoạt động như kiểu giá trị. Nó có gây hiểu lầm không?Tại sao các loại vô danh Bằng thực hiện so sánh các trường?

class Person 
    { 
     public string Name { get; set; } 
     public int Age { get; set; } 
    } 

    public static void ProofThatAnonymousTypesEqualsComparesBackingFields() 
    { 
     var personOne = new { Name = "Paweł", Age = 18 }; 
     var personTwo = new { Name = "Paweł", Age = 18 }; 

     Console.WriteLine(personOne == personTwo); // false 
     Console.WriteLine(personOne.Equals(personTwo)); // true 
     Console.WriteLine(Object.ReferenceEquals(personOne, personTwo)); // false 

     var personaOne = new Person { Name = "Paweł", Age = 11 }; 
     var personaTwo = new Person { Name = "Paweł", Age = 11 }; 
     Console.WriteLine(personaOne == personaTwo); // false 
     Console.WriteLine(personaOne.Equals(personaTwo)); // false 
     Console.WriteLine(Object.ReferenceEquals(personaOne, personaTwo)); // false 
    } 

Thoạt nhìn, tất cả giá trị boolean được in phải sai. Nhưng các dòng với Equals gọi trả về các giá trị khác nhau khi loại Person được sử dụng, và loại vô danh được sử dụng.

+1

Đây là một trong số ít bài đăng trên SO có 3 câu trả lời trở lên, trong đó đại diện trung bình. của những người trả lời là hơn ** 95k **, từ tháng 5 đến 23-2017. – dotNET

Trả lời

50

Trường hợp loại ẩn danh là các giá trị dữ liệu không thay đổi mà không có hành vi hoặc nhận dạng. Nó không có ý nghĩa gì khi so sánh chúng với nhau. Trong bối cảnh đó, tôi nghĩ rằng nó là hoàn toàn hợp lý để tạo ra sự so sánh cấu trúc bình đẳng cho họ.

Nếu bạn muốn chuyển đổi hành vi so sánh thành tùy chỉnh nào đó (so sánh tham chiếu hoặc không phân biệt chữ hoa chữ thường), bạn có thể sử dụng Resharper để chuyển kiểu ẩn danh thành một lớp có tên. Resharper cũng có thể tạo ra các thành viên bình đẳng.

Ngoài ra còn có một lý do rất thực tế để làm điều này: Các loại ẩn danh thuận tiện để sử dụng làm khóa băm trong LINQ tham gia và nhóm. Vì lý do đó, chúng yêu cầu thực hiện đúng ngữ nghĩa EqualsGetHashCode.

+0

Tôi vẫn còn bối rối vì sao các loại ẩn danh == không so sánh các giá trị như .Equals? –

+10

Bởi vì == không gọi Equals trong C# (không bao giờ!). Nó gọi 'operator ==' và các kiểu vô danh không có cái đó. Vì vậy, C# sử dụng bình đẳng tham chiếu .; Nó tốt hay xấu? Ai biết được vì nó là một sự đánh đổi. Sau khi tất cả, tôi chưa bao giờ cảm thấy sự cần thiết phải so sánh hai trường hợp loại vô danh. Không bao giờ xảy ra vì một lý do nào đó. – usr

+1

Tôi đồng ý rằng nó thông minh từ góc độ thuận tiện (GroupBy() v.v.), nhưng có một chút lừa đảo từ góc độ kỹ thuật. MSDN nói "Các kiểu ẩn danh là các kiểu lớp lấy trực tiếp từ đối tượng ...". Tuy nhiên đối tượng không thực hiện IStructuralEquatable, do đó giao diện bằng cách nào đó được tiêm đằng sau màn cửa, làm cho mọi thứ hơi khó hiểu (mặc dù hữu ích) ... – Anders

31

Đối với lý do tại sao phần bạn nên yêu cầu các nhà thiết kế ngôn ngữ ...

Nhưng tôi thấy điều này trong bài viết Eric Lippert về Anonymous Types Unify Within An Assembly, Part Two

Một loại vô danh cung cấp cho bạn một nơi thuận tiện để lưu trữ một nhỏ tập hợp các cặp tên/giá trị không thay đổi, nhưng nó mang lại cho bạn nhiều hơn thế. Nó cũng cung cấp cho bạn một cách thực hiện Equals, GetHashCode và, hầu hết germane để thảo luận này, ToString. (*)

Trong trường hợp lý do tại sao một phần đi kèm trong các lưu ý:

(*) Chúng tôi cung cấp cho bạn Equals và GetHashCode để bạn có thể sử dụng trường hợp các loại vô danh trong các truy vấn LINQ như phím trên đó để thực hiện các chuyến tham gia . LINQ to Objects thực hiện các phép nối bằng cách sử dụng bảng băm cho các lý do hiệu suất và do đó chúng tôi cần triển khai chính xác Bằng và GetHashCode.

12

Câu trả lời chính thức từ C# Language Specification (có thể đạt được here):

Các Equals và phương pháp GetHashCode trên các loại vô danh ghi đè lên các phương pháp kế thừa từ đối tượng, và được quy định trong điều khoản của Equals và GetHashCode của các thuộc tính, sao cho hai trường hợp cùng loại ẩn danh là bằng và nếu chỉ khi tất cả các thuộc tính của chúng bằng nhau.

(nhấn mạnh của tôi)

Những câu trả lời khác giải thích tại sao điều này được thực hiện.

Điều đáng chú ý là trong VB.Net số implementation is different:

Ví dụ về loại ẩn danh không có thuộc tính khóa chỉ bằng chính nó.

Thuộc tính quan trọng phải được chỉ định rõ ràng khi tạo đối tượng kiểu ẩn danh. Mặc định là: không có khóa, điều này có thể rất khó hiểu đối với người dùng C#!

Những đối tượng không phải là bình đẳng trong VB, nhưng sẽ là trong C# mã -equivalent:

Dim prod1 = New With {.Name = "paperclips", .Price = 1.29} 
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29} 

Những đối tượng đánh giá để "bình đẳng":

Dim prod3 = New With {Key .Name = "paperclips", .Price = 1.29} 
Dim prod4 = New With {Key .Name = "paperclips", .Price = 2.00} 
8

Bởi vì nó mang lại cho chúng tôi cái gì đó hữu ích. Hãy xem xét những điều sau đây:

var countSameName = from p in PersonInfoStore 
    group p.Id by new {p.FirstName, p.SecondName} into grp 
    select new{grp.Key.FirstName, grp.Key.SecondName, grp.Count()}; 

Các công trình vì việc thực hiện Equals()GetHashCode() với nhiều loại vô danh hoạt động trên cơ sở các lĩnh vực-by-lĩnh vực bình đẳng.

  1. Điều này có nghĩa là ở trên sẽ gần giống với truy vấn tương tự khi chạy với số PersonInfoStore không phải là đối tượng LINQ. (Vẫn không giống nhau, nó sẽ khớp với những gì một nguồn XML sẽ làm, nhưng không phải những gì mà hầu hết các collations của cơ sở dữ liệu sẽ dẫn đến).
  2. Nó có nghĩa là chúng ta không cần phải xác định một IEqualityComparer cho mỗi cuộc gọi đến GroupBy đó sẽ làm cho nhóm bởi thực sự khó khăn với các đối tượng ẩn danh - đó là có thể, nhưng không dễ dàng để xác định một IEqualityComparer cho các đối tượng ẩn danh - và xa ý nghĩa tự nhiên nhất .
  3. Trên tất cả, nó không gây ra sự cố với hầu hết các trường hợp.

Điểm thứ ba đáng xem.

Khi xác định loại giá trị, chúng tôi tự nhiên muốn có khái niệm bình đẳng dựa trên giá trị. Mặc dù chúng ta có thể có một ý tưởng khác về sự bình đẳng dựa trên giá trị đó so với mặc định, chẳng hạn như khớp một trường cụ thể không phân biệt, mặc định là tự nhiên hợp lý (nếu hiệu suất kém và lỗi trong một trường hợp *). (Ngoài ra, bình đẳng tham chiếu là vô nghĩa trong trường hợp này).

Khi xác định loại tham chiếu, chúng tôi có thể hoặc không muốn có khái niệm bình đẳng dựa trên giá trị. Mặc định cho chúng ta sự bình đẳng tham chiếu, nhưng chúng ta có thể dễ dàng thay đổi điều đó. Nếu chúng tôi thay đổi nó, chúng tôi có thể thay đổi nó chỉ EqualsGetHashCode hoặc cho họ và cũng ==.

Khi chúng tôi xác định loại ẩn danh, hãy đợi, chúng tôi đã không xác định, đó là điều vô danh! Hầu hết các kịch bản mà chúng tôi quan tâm về bình đẳng tham chiếu không còn nữa. Nếu chúng ta sẽ giữ một vật thể xung quanh đủ lâu để sau này tự hỏi liệu nó có giống với một vật thể khác không, chúng ta có lẽ sẽ không đối phó với một vật thể vô danh nào. Các trường hợp mà chúng tôi quan tâm về bình đẳng dựa trên giá trị xuất hiện rất nhiều. Rất thường với LINQ (GroupBy như chúng ta đã thấy ở trên, mà còn Distinct, Union, GroupJoin, Intersect, SequenceEqual, ToDictionaryToLookup) và thường với mục đích khác (nó không giống như chúng tôi đã không làm những điều LINQ làm cho chúng ta với enumerables trong 2.0 và ở một mức độ nào đó trước đó, bất kỳ ai viết mã bằng 2.0 có thể đã viết một nửa các phương pháp trong số Enumerable mình).

Trong tất cả, chúng tôi thu được rất nhiều từ cách thức hoạt động bình đẳng với các lớp ẩn danh.

Trong cơ hội không có ai đó thực sự muốn tham chiếu bình đẳng, == bằng cách sử dụng bình đẳng tham chiếu có nghĩa là họ vẫn có điều đó, vì vậy chúng tôi không mất bất cứ điều gì. Đó là con đường để đi.

* Việc triển khai mặc định Equals()GetHashCode() có tối ưu hóa cho phép sử dụng kết hợp nhị phân trong trường hợp an toàn để thực hiện việc này. Thật không may có một lỗi mà làm cho nó đôi khi mis-xác định một số trường hợp là an toàn cho phương pháp này nhanh hơn khi họ không (hoặc ít nhất là nó được sử dụng để, có thể nó đã được cố định). Một trường hợp phổ biến là nếu bạn có trường decimal, trong một cấu trúc, thì nó sẽ xem xét một số trường hợp có trường tương đương là không bằng nhau.

+1

Nếu không có sự thực hiện bình đẳng này, việc nhóm không thể thực hiện được trên các kiểu ẩn danh. –

+0

@PeterRitchie không phải là những gì tôi vừa nói? –