2012-01-20 24 views
14

Giản từ this question và đã thoát khỏi khả năng ảnh hưởng đến từ LinqPad (không offsensive), một giao diện điều khiển ứng dụng đơn giản như thế này:nhóm phương pháp Implicit chuyển đổi Gotcha (Phần 2)

public class Program 
{ 
    static void M() { }  
    static void Main(string[] args) 
    { 
     Action a = new Action(M); 
     Delegate b = new Action(M); 
     Console.WriteLine(a == b);  //got False here 
     Console.Read(); 
    }   
} 

Các "false" Kết quả từ các nhà điều hành ceq trong CIL của mã ở trên (truy cập câu hỏi gốc để biết chi tiết). Vì vậy, câu hỏi của tôi là:

(1) Tại sao == đang dịch sang ceq thay vì call Delegate Equals?

Ở đây tôi không quan tâm đến (un) gói giữa Đại biểu và Hành động. Cuối cùng, khi đánh giá a == b, một là loại Action trong khi b là Delegate. Từ spec:

7.3.4 hành Binary quá tải độ phân giải

Một hoạt động có dạng x op y, trong đó op là một nhà điều hành nhị phân overloadable, x là một biểu hiện loại X, và y là một biểu thức loại Y, được xử lý như sau:

• Tập hợp các toán tử do người dùng xác định do X và Y cung cấp cho toán tử vận ​​hành op (x, y) được xác định. Bộ này bao gồm các liên kết của các toán tử ứng cử viên được cung cấp bởi X và các ứng cử viên toán tử do Y cung cấp, mỗi xác định sử dụng các quy tắc của §7.3.5. Nếu X và Y là cùng một loại hoặc nếu X và Y có nguồn gốc từ loại cơ sở chung là thì các toán tử ứng viên được chia sẻ chỉ xuất hiện trong kết hợp được đặt một lần.

• Nếu tập hợp các toán tử do người dùng xác định người dùng không phải là trống, thì điều này sẽ trở thành tập hợp toán tử ứng viên cho hoạt động . Nếu không, toán tử nhị phân được xác định trước op triển khai, bao gồm các biểu mẫu được dỡ bỏ của chúng, trở thành tập hợp các toán tử ứng cử viên cho hoạt động này. Việc triển khai được xác định trước của một toán tử đã cho được xác định trong mô tả của toán tử (từ 7,8 đến §7,12).

• Các quy tắc giải quyết tình trạng quá tải của §7.5.3 được áp dụng cho các tập hợp các toán ứng cử viên để lựa chọn các nhà điều hành tốt nhất đối với danh sách đối số (x, y) với, và điều hành này trở nên kết quả của quá trình giải quyết quá tải. Nếu độ phân giải quá tải không chọn được một toán tử tốt nhất, một lỗi thời gian ràng buộc sẽ xảy ra.

7.3.5 Ứng viên điều hành người dùng định nghĩa

Cho một loại T và một op điều hành hoạt động (A), nơi op là một nhà điều hành overloadable và A là một danh sách đối số, các thiết lập của ứng cử viên khai thác người dùng định nghĩa được cung cấp bởi T cho nhà điều hành op (A) được xác định như sau:

• Xác định loại T0. Nếu T là một loại nullable, T0 là loại cơ bản của nó, nếu không T0 bằng T.

• Đối với tất cả các tờ khai hành op trong T0 và tất cả các nâng hình thức khai thác như vậy, nếu ít nhất một nhà điều hành được áp dụng (§7.5.3.1) đối với danh sách đối số A, sau đó tập hợp các toán tử ứng cử viên bao gồm tất cả các toán tử áp dụng như vậy trong T0.

• Nếu không, nếu T0 là đối tượng, tập hợp toán tử ứng viên sẽ trống.

• Nếu không, tập hợp toán tử ứng cử viên do T0 cung cấp là tập hợp của toán tử ứng cử viên được cung cấp bởi lớp cơ sở trực tiếp T0 hoặc lớp cơ sở hiệu quả T0 nếu T0 là tham số kiểu.

Từ spec, a và b có một lớp cơ sở cùng Delegate, rõ ràng các quy tắc điều hành == quy định tại Delegate nên được áp dụng ở đây (các toán tử == gọi Delegate.Equals cơ bản). Nhưng bây giờ có vẻ như danh sách ứng cử viên của các toán tử do người dùng xác định trống và cuối cùng là Object == được áp dụng.

(2) Nên (không) mã FCL tuân theo thông số ngôn ngữ C#? Nếu không, câu hỏi đầu tiên của tôi là vô nghĩa bởi vì một cái gì đó được điều trị đặc biệt. Và sau đó chúng tôi có thể trả lời tất cả những câu hỏi này bằng cách sử dụng "oh, đó là một điều trị đặc biệt trong FCL, họ có thể làm điều gì đó mà chúng tôi không thể làm. Thông số này dành cho các lập trình viên bên ngoài, đừng ngốc nghếch".

+0

Đó là lý do tại sao tốt nhất nên sử dụng 'Bằng’ khi mong đợi ngữ nghĩa loại giá trị. Bởi vì (có khả năng) quá tải nhà điều hành bị hỏng. – Groo

+0

@Groo: Chính xác. Và bằng cách tôi nhận được cảnh báo biên dịch cho mã trong câu hỏi 'Có thể so sánh tham chiếu không mong muốn; để so sánh giá trị, hãy bỏ phía bên phải để nhập 'System.Action''. –

+0

Đó chắc chắn là để làm với việc xử lý đặc biệt của đại biểu nói chung, như cố gắng điều tương tự với một hệ thống phân cấp lớp người dùng định nghĩa phương thức '==' tùy chỉnh – AakashM

Trả lời

4

Có hai loại toán tử: toán tử do người dùng xác định và toán tử được xác định trước. Phần 7.3.5 "Toán tử do người dùng xác định ứng cử viên" không áp dụng cho các toán tử được xác định trước. Ví dụ, các toán tử trên decimal trông giống như các toán tử do người dùng định nghĩa trong trình biên dịch ngược, nhưng C# coi chúng là toán tử được xác định trước và áp dụng khuyến mãi số cho chúng (khuyến mãi số không được áp dụng cho toán tử do người dùng xác định).

Mục 7.10.8 "Toán tử bình đẳng đại biểu" xác định operator ==(Delegate, Delegate) là toán tử định sẵn, vì vậy tôi nghĩ rằng tất cả quy tắc về toán tử do người dùng xác định không áp dụng cho toán tử này (mặc dù điều này không rõ ràng 100% trong thông số như trong trường hợp này, toán tử được xác định trước không áp dụng bất cứ khi nào toán tử do người dùng xác định).

Every delegate type implicitly provides the following predefined comparison operators: 
bool operator ==(System.Delegate x, System.Delegate y); 
bool operator !=(System.Delegate x, System.Delegate y); 

Nhưng System.Delegate bản thân không được coi là một loại đại biểu, vì vậy các ứng cử viên duy nhất để giải quyết tình trạng quá tải là operator ==(object, object).

+1

Sau đó, tại sao đầu ra thay đổi thành 'True' nếu anh ta thay đổi thành' Console.WriteLine ((Delegate) a == b); '? Bây giờ kiểu thời gian biên dịch của toán hạng không phải là "kiểu ủy nhiệm" (vì bạn nói lớp không bê tông 'System.Delegate' không phải là" kiểu ủy nhiệm "), nhưng vẫn sử dụng quá tải kiểu ủy nhiệm. (Tôi chỉ tìm thấy chủ đề này vì câu hỏi của riêng tôi [Độ phân giải quá tải trên toán tử == với các loại biến thể chung của đại biểu] (http://stackoverflow.com/questions/22408165/) đã được liên kết với chủ đề này.) –

-1

Chìa khóa ở đây là toán tử == và phương pháp Equals cho loại Delegate là hai thứ khác nhau. Đối với các loại tham chiếu, == sẽ xem liệu cả hai tham chiếu có trỏ đến cùng một đối tượng hay không trừ khi toán tử == bị ghi đè (xem: == Operator (C#)).

Vì bạn đang tạo ra hai Action đối tượng khác nhau, mặc dù họ trong nội bộ gọi cùng một phương pháp, họ là những đối tượng khác nhau tại các địa điểm khác nhau trong bộ nhớ, và không phải là một giá trị hoặc string loại, vì vậy == là trong trường hợp này một ReferenceEquals và không gọi phương thức Delegate.Equals, đã được ghi đè để xem liệu hai đối tượng có thực hiện tương tự không. Đối với các loại tham chiếu khác ngoài string, đây là hành vi mặc định của == hoặc Equals.

+1

Đối với kiểu tham chiếu, toán tử '==' làm bất cứ điều gì được định nghĩa làm cho loại cụ thể đó.Nhiều loại không thực hiện kiểm tra bình đẳng tham chiếu. –

+0

Tôi đã thêm điều khoản 'trừ khi "==" toán tử bị ghi đè' vào câu lệnh của tôi. Điều quan trọng là op = "==" thường được sử dụng để so sánh tham chiếu và phương thức Equals thường được sử dụng để so sánh giá trị. – SamuelWarren

+1

@highphilospher: Tôi đang hoàn tác downvote, nhưng C# không chia sẻ triết lý của Java về '==' được sử dụng cho bình đẳng tham chiếu. Chủ yếu là nó không phải là. Nếu bạn muốn tham chiếu bình đẳng, hãy sử dụng phương thức tĩnh 'object.ReferenceEquals'. –

5

Trình biên dịch hoạt động rất khác biệt và khác thường với các đại biểu. Có rất nhiều xử lý ngầm. Lưu ý rằng quy tắc 'loại cơ sở chung' trong hướng dẫn này được áp dụng cho 'nhà khai thác do người dùng xác định'. Các đại biểu là nội bộ và hệ thống. Ví dụ: bạn có thể viết Action a = M; thay vì Action a = new Action(M);. Và bạn có thể thêm a += M; sau đó. Kiểm tra những gì xảy ra trong CIL, nó là thú vị ở lần đầu tiên.

Thêm nữa: thật nguy hiểm và không tầm thường để so sánh các đại biểu. Mỗi đại biểu thực sự là đại biểu multicast. Bạn có thể thêm một số con trỏ hàm cho cùng một đại biểu. Đại biểu có [L(); M(); N();] tương đương với đại biểu [M();] không? Con trỏ hàm chứa cá thể lớp (ví dụ phương thức). [a.M();] có bằng [b.M();] không? Tất cả điều đó phụ thuộc vào một trường hợp và việc triển khai so sánh yêu cầu phải trải qua danh sách yêu cầu.

Ủy nhiệm kế thừa từ loại cơ sở chung Ủy quyền là ẩn và vấn đề này bạn có thể gặp phải trong các tình huống khác, ví dụ: ràng buộc chung: bạn không thể chỉ định Đại biểu như một ràng buộc đối với tham số chung T. Ở đây trình biên dịch từ chối rõ ràng điều này. Điều tương tự về việc tạo các lớp học của riêng bạn, được kế thừa từ Đại biểu.

Đây là câu trả lời cho cả hai câu hỏi - 'Ủy quyền' không hoàn toàn là FCL, nó được kết hợp chặt chẽ với trình biên dịch.Nếu bạn thực sự muốn hành vi so sánh đại biểu của Microsoft - chỉ cần gọi rõ ràng Equals(a, b)

+0

Đây là một câu hỏi về đặc tả ngôn ngữ, và tôi không nghĩ rằng "Đây là ma thuật biên dịch" là một câu trả lời đầy đủ cho những loại câu hỏi đó. – Daniel

+0

Và có, so sánh các đại biểu multicast là không tầm thường, nhưng đặc tả C# xác định rõ cách thức hoạt động của nó. – Daniel

3

cảnh báo CS0253: Có thể so sánh tham chiếu không mong muốn; để so sánh giá trị, hãy bỏ phía bên phải để nhập 'System.Action'

Đó là cảnh báo bạn nhận được cho mã C# đó. Đừng bỏ qua cảnh báo đó, nhóm C# đã nhận thức rõ rằng mã họ đã tạo cho so sánh này là bất ngờ. Họ không để tạo mã đó, họ có thể dễ dàng thực hiện những gì bạn mong đợi. Giống như mã này không:

Module Module1 
    Sub M() 
    End Sub 

    Sub Main() 
     Dim a = New Action(AddressOf M) 
     Dim b = DirectCast(New Action(AddressOf M), [Delegate]) 
     Console.WriteLine(a = b)  ''got True here 
     Console.Read() 
    End Sub 
End Module 

nào tạo ra gần như giống nhau MSIL, ngoại trừ việc thay vì ceq bạn nhận được:

IL_001d: call bool [mscorlib]System.Delegate::op_Equality(class [mscorlib]System.Delegate, 
                  class [mscorlib]System.Delegate) 

Những gì bạn hy vọng mã C# sẽ làm gì. Đó là mã VB.NET, trong trường hợp bạn không nhận ra nó. Nếu không, lý do mà Microsoft giữ hai ngôn ngữ được quản lý lớn xung quanh, mặc dù chúng có khả năng rất giống nhau. Nhưng với sự lựa chọn khả năng sử dụng rất khác nhau. Bất cứ khi nào có nhiều cách để tạo mã, nhóm C# luôn chọn cho hiệu suất , nhóm VB.NET luôn phù hợp cho tiện lợi.

Và hiệu suất chắc chắn là chìa khóa ở đây, so sánh các đối tượng đại biểu là đắt tiền. Các quy tắc được nêu trong Ecma-335, phần II.14.6.1. Nhưng bạn có thể lý do nó ra cho chính mình, có rất nhiều kiểm tra để làm. Nó cần kiểm tra xem đối tượng đích của đại biểu có tương thích hay không. Và đối với mỗi đối số, nó phải kiểm tra xem giá trị có được chuyển đổi hay không. Chi phí mà đội C# không muốn ẩn.

Và không, bạn sẽ nhận được cảnh báo nhắc nhở bạn rằng họ đã thực hiện lựa chọn không trực quan. .

+0

Điều đáng ngạc nhiên nhất trong C# là trong khi cả hai '(Hành động) a == (Hành động) b' và' (Đại biểu) a == (Đại biểu) b' đi đến phép so sánh giá trị đại biểu, trường hợp '(Hành động) a == (Đại biểu) b 'đi đến kiểm tra bình đẳng tham chiếu (với cảnh báo thời gian biên dịch). Điều này là không tự nhiên, đặc biệt vì 'Hành động' được chuyển đổi hoàn toàn (bằng chuyển đổi tham chiếu) thành' Đại biểu'. –

+0

Bạn nhận được một nửa số tiền thưởng cho câu trả lời của bạn (điều này rất tốt). Nhưng tôi vẫn cảm thấy rằng trình biên dịch C# thực hiện một cái gì đó mà không dễ dàng thấy được trong thỏa thuận với Đặc tả Ngôn ngữ C#, và tôi muốn có câu trả lời về điều đó. Ngoài ra, hãy xem độ phân giải _Overload thread được liên kết trên toán tử '==' với biến thể loại đại biểu chung_. Nhưng tôi đồng ý rằng so sánh các đại biểu multicast là tốn kém, và kiểm tra bình đẳng tham chiếu là rất rẻ, và rằng có thể có những triết lý khác nhau trong C# và VB.NET cho những gì để "tối ưu hóa". –

Các vấn đề liên quan