Tại sao việc thêm một phương thức sẽ thêm một cuộc gọi không rõ ràng, nếu nó không liên quan đến sự mơ hồ


112

Tôi có lớp học này

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] something)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }
}

Nếu tôi gọi nó như thế này:

        var blah = new Overloaded();
        blah.ComplexOverloadResolution("Which wins?");

Nó ghi Normal Winnervào bảng điều khiển.

Nhưng, nếu tôi thêm một phương thức khác:

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }

Tôi nhận được lỗi sau đây:

Lời gọi không rõ ràng giữa các phương thức hoặc thuộc tính sau:> ' Overloaded.ComplexOverloadResolution(params string[])' và ' Overloaded.ComplexOverloadResolution<string>(string)'

Tôi có thể hiểu rằng việc thêm một phương thức có thể gây ra sự mơ hồ về cuộc gọi, nhưng đó là sự mơ hồ giữa hai phương thức đã tồn tại (params string[])<string>(string)! Rõ ràng cả hai phương thức liên quan đến sự mơ hồ đều không phải là phương thức mới được thêm vào, vì phương thức đầu tiên là tham số và phương thức thứ hai là phương thức chung.

Đây có phải là một lỗi? Phần nào của thông số kỹ thuật nói rằng đây là trường hợp?


2
Tôi không nghĩ rằng 'Overloaded.ComplexOverloadResolution(string)'đề cập đến <string>(string)phương pháp; Tôi nghĩ rằng nó đề cập đến (string, object)phương pháp không có đối tượng được cung cấp.
phoog

1
@phoog Ồ, dữ liệu đó đã bị StackOverflow cắt vì đó là thẻ, nhưng thông báo lỗi có trình chỉ định mẫu. Tôi đang thêm nó trở lại.
McKay

bạn đã bắt tôi ra ngoài! Tôi đã trích dẫn các phần liên quan của thông số kỹ thuật trong câu trả lời của mình, nhưng tôi đã không dành nửa giờ qua để đọc và hiểu chúng!
phoog

@phoog, nhìn qua những phần đó của thông số kỹ thuật, tôi không thấy gì về việc đưa sự mơ hồ vào các phương thức khác với chính nó và một phương thức khác, không phải hai phương thức khác.
McKay

Tôi nghĩ rằng đây chỉ là trò oẳn tù tì : bất kỳ bộ nào có hai giá trị khác nhau đều có người chiến thắng, nhưng bộ ba giá trị hoàn chỉnh thì không.
phoog

Câu trả lời:


107

Đây có phải là một lỗi?

Đúng.

Xin chúc mừng, bạn đã tìm thấy một lỗi trong giải quyết quá tải. Lỗi tái tạo trong C # 4 và 5; nó không tái tạo trong phiên bản "Roslyn" của trình phân tích ngữ nghĩa. Tôi đã thông báo cho nhóm kiểm tra C # 5 và hy vọng chúng tôi có thể điều tra và giải quyết vấn đề này trước khi phát hành cuối cùng. (Như mọi khi, không có lời hứa.)

Một phân tích đúng sau đây. Các ứng cử viên là:

0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string) 
3: C(string, object) 

Ứng cử viên số 0 rõ ràng là không thể áp dụng được vì stringkhông thể chuyển đổi thành string[]. Điều đó để lại ba.

Trong số ba, chúng tôi phải xác định một phương pháp tốt nhất duy nhất. Chúng tôi làm như vậy bằng cách so sánh từng cặp của ba ứng cử viên còn lại. Có ba cặp như vậy. Tất cả chúng đều có danh sách thông số giống hệt nhau khi chúng tôi loại bỏ các thông số tùy chọn bị bỏ qua, có nghĩa là chúng tôi phải đi đến vòng kết thúc nâng cao được mô tả trong phần 7.5.3.2 của đặc điểm kỹ thuật.

Cái nào tốt hơn, 1 hay 2? Điểm then chốt có liên quan là một phương pháp chung luôn kém hơn một phương pháp không chung chung. 2 kém hơn 1. Vì vậy 2 không thể là người chiến thắng.

Cái nào tốt hơn, 1 hay 3? Sự ràng buộc có liên quan là: một phương pháp chỉ áp dụng ở dạng mở rộng của nó luôn kém hơn một phương pháp áp dụng ở dạng bình thường. Do đó 1 kém hơn 3. Vì vậy 1 không thể là người chiến thắng.

Cái nào tốt hơn, 2 hay 3? Điểm then chốt có liên quan là một phương pháp chung luôn kém hơn một phương pháp không chung chung. 2 kém hơn 3. Vì vậy 2 không thể là người chiến thắng.

Để được chọn từ một tập hợp nhiều ứng viên thích hợp, ứng viên phải (1) bất bại, (2) đánh bại ít nhất một ứng viên khác và (3) là ứng cử viên duy nhất có hai thuộc tính đầu tiên. Ứng cử viên ba không bị đánh bại bởi ứng viên khác và đánh bại ít nhất một ứng cử viên khác; nó là ứng cử viên duy nhất có tài sản này. Do đó, ứng cử viên ba là ứng cử viên tốt nhất duy nhất . Nó sẽ thắng.

Không chỉ trình biên dịch C # 4 bị sai, như bạn lưu ý chính xác là nó đang báo cáo một thông báo lỗi kỳ lạ. Việc trình biên dịch nhận được sai phân tích độ phân giải quá tải là một điều hơi đáng ngạc nhiên. Việc nhận được thông báo lỗi sai là hoàn toàn không có gì đáng ngạc nhiên; heuristic lỗi "phương pháp mơ hồ" về cơ bản chọn bất kỳ hai phương pháp nào từ tập hợp ứng viên nếu không thể xác định được phương pháp tốt nhất. Nó không phải là rất tốt trong việc tìm ra sự mơ hồ "thực sự", nếu thực tế là có một.

Người ta có thể hỏi một cách hợp lý tại sao lại như vậy. Khá khó để tìm ra hai phương thức "không rõ ràng một cách rõ ràng" bởi vì quan hệ "tốt hơn" là không bên trong . Có thể xảy ra tình huống ứng viên 1 tốt hơn 2, 2 tốt hơn 3 và 3 tốt hơn 1. Trong những tình huống như vậy, chúng ta không thể làm tốt hơn là chọn hai trong số họ là "những người mơ hồ".

Tôi muốn cải thiện kinh nghiệm này cho Roslyn nhưng mức độ ưu tiên thấp.

(Bài tập dành cho độc giả: "Phát triển một thuật toán thời gian tuyến tính để xác định thành viên tốt nhất duy nhất của tập hợp n phần tử trong đó quan hệ tốt hơn là không trực tiếp" là một trong những câu hỏi tôi được hỏi vào ngày tôi phỏng vấn cho nhóm này. Nó không phải một thuật toán rất khó; hãy thử.)

Một trong những lý do tại sao chúng tôi đã lùi lại việc thêm các đối số tùy chọn vào C # trong thời gian dài là số lượng các tình huống phức tạp không rõ ràng mà nó đưa vào thuật toán giải quyết quá tải; rõ ràng là chúng tôi đã không hiểu đúng.

Nếu bạn muốn nhập vấn đề Kết nối để theo dõi, vui lòng. Nếu bạn chỉ muốn chúng tôi chú ý đến, hãy coi như đã xong. Tôi sẽ tiếp tục thử nghiệm vào năm tới.

Cảm ơn vì đã mang đến sự chú ý của tôi. Xin lỗi vì lỗi.


1
Cảm ơn vì sự trả lời. bạn nói "1 tệ hơn 2", nhưng nó chọn phương pháp 1 nếu tôi chỉ có phương pháp 1 và 2?
McKay

@McKay: Rất tiếc, bạn nói đúng, tôi đã tuyên bố ngược lại. Tôi sẽ sửa văn bản.
Eric Lippert

1
Thật khó xử khi đọc cụm từ "thời gian còn lại của năm" khi không còn nửa tuần nữa :)
BoltClock

2
@BoltClock thực sự, tuyên bố "nghỉ cho phần còn lại của năm" ngụ ý một ngày nghỉ.
phoog

1
Tôi nghĩ vậy. Tôi đọc "3) là ứng cử viên duy nhất có hai thuộc tính đầu tiên""ứng cử viên duy nhất (bất bại và đánh bại ít nhất một ứng viên khác)" . Nhưng nhận xét gần đây nhất của bạn khiến tôi nghĩ rằng "(hãy là ứng cử viên duy nhất bất bại) và đánh bại ít nhất một ứng cử viên khác" . Tiếng Anh thực sự có thể sử dụng các ký hiệu nhóm. Nếu điều sau là đúng, sau đó tôi nhận được nó một lần nữa.
default.kramer

5

Phần nào của thông số kỹ thuật nói rằng đây là trường hợp?

Mục 7.5.3 (giải quyết quá tải), cùng với các mục 7.4 (tra cứu thành viên) và 7.5.2 (suy luận kiểu).

Đặc biệt lưu ý phần 7.5.3.2 (thành viên hàm tốt hơn), trong phần này nói rằng "các tham số tùy chọn không có đối số tương ứng bị xóa khỏi danh sách tham số" và "Nếu M (p) là một phương thức không chung chung thì amd M (q) là một phương pháp chung chung thì M (p) tốt hơn M (q). "

Tuy nhiên, tôi không hiểu các phần này của thông số kỹ lưỡng đủ để biết phần nào của thông số kỹ thuật kiểm soát hành vi này, chưa nói đến việc đánh giá xem nó có tuân thủ hay không.


Nhưng điều đó không giải thích tại sao việc thêm một thành viên sẽ gây ra sự mơ hồ giữa hai phương thức đã tồn tại.
McKay

@McKay đủ công bằng (xem chỉnh sửa). Chúng tôi sẽ chỉ phải chờ cho Eric Lippert cho chúng tôi biết đây là hành vi đúng: ->
phoog

1
Đó là những phần bên phải của thông số kỹ thuật. Rắc rối là họ nói rằng không nên như vậy!
Eric Lippert

3

bạn có thể tránh sự mơ hồ này bằng cách thay đổi tên của tham số đầu tiên trong một số phương thức và chỉ định tham số mà bạn muốn gán

như thế này :

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] somethings)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Overloaded a = new Overloaded();
        a.ComplexOverloadResolution(something:"asd");
    }
}

Ồ, tôi biết mã này không tốt, và có một số cách để giải quyết nó, câu hỏi là "tại sao trình biên dịch lại hoạt động theo cách này?"
McKay

1

Nếu bạn xóa paramskhỏi phương pháp đầu tiên của mình, điều này sẽ không xảy ra. Phương thức thứ nhất và thứ ba của bạn có cả hai lệnh gọi hợp lệ ComplexOverloadResolution(string), nhưng nếu phương thức đầu tiên của bạn là phương thức public void ComplexOverloadResolution(string[] something)sẽ không có sự mơ hồ.

Việc cung cấp giá trị cho một tham số object somethingElse = nulllàm cho nó trở thành tham số tùy chọn và do đó nó không phải được chỉ định khi gọi quá tải đó.

Chỉnh sửa: Trình biên dịch đang làm một số thứ điên rồ ở đây. Nếu bạn di chuyển phương thức thứ ba trong mã sau phương thức đầu tiên, nó sẽ báo cáo chính xác. Vì vậy, có vẻ như nó đang lấy hai lần quá tải đầu tiên và báo cáo chúng, mà không kiểm tra xem có đúng không.

'ConsoleApplication1.Program.ComplexOverloadResolution (params string [])' và 'ConsoleApplication1.Program.ComplexOverloadResolution (string, object)'

Edit2: Phát hiện mới. Loại bỏ bất kỳ phương thức nào khỏi ba phương thức trên sẽ không tạo ra sự mơ hồ giữa hai phương thức. Vì vậy, có vẻ như xung đột chỉ xuất hiện nếu có mặt ba phương pháp, bất kể thứ tự.


Nhưng điều đó không giải thích tại sao việc thêm một thành viên sẽ gây ra sự mơ hồ giữa hai phương thức đã tồn tại.
McKay

Sự mơ hồ xảy ra giữa phương thức đầu tiên và phương thức thứ ba của bạn, nhưng tại sao trình biên dịch lại báo cáo hai phương thức kia thì không phải là tôi.
Tomislav Markovski

Nhưng nếu tôi loại bỏ phương thức thứ hai, tôi không có sự mơ hồ, nó gọi thành công phương thức thứ ba. Vì vậy, có vẻ như trình biên dịch không có sự mơ hồ giữa phương thức thứ nhất và thứ ba.
McKay

Xem Chỉnh sửa của tôi. Trình biên dịch điên rồ.
Tomislav Markovski

Trên thực tế, bất kỳ sự kết hợp nào của hai phương pháp đều không tạo ra sự mơ hồ. Điều này rất kỳ quặc. Chỉnh sửa2.
Tomislav Markovski

1
  1. Nếu bạn viết

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");
    

    hoặc chỉ viết

    var blah = new Overloaded();
    blah.ComplexOverloadResolution();
    

    nó sẽ kết thúc vào cùng một phương thức , trong phương thức

    public void ComplexOverloadResolution(params string[] something

    Đây là paramstừ khóa nguyên nhân tạo ra từ khóa này đối sánh tốt nhất đối với trường hợp không có tham số được chỉ định

  2. Nếu bạn cố gắng thêm yuor phương thức mới như thế này

    public void ComplexOverloadResolution(string something)
    {
        Console.WriteLine("Added Later");
    }
    

    Nó sẽ biên dịch hoàn hảo và gọi phương thức này vì nó là một kết hợp hoàn hảo cho cuộc gọi của bạn với một stringtham số. Sau đó mạnh hơn nhiều params string[] something.

  3. Bạn khai báo phương thức thứ hai giống như bạn đã làm

    public void ComplexOverloadResolution(string something, object something=null);

    Trình biên dịch, hoàn toàn nhầm lẫn giữa phương thức đầu tiên và phương thức này, chỉ cần thêm một phương thức. Bởi vì nó không biết bây giờ anh ấy sẽ thực hiện chức năng nào trong cuộc gọi của bạn

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");
    

    Trong thực tế, nếu bạn xóa tham số chuỗi khỏi cuộc gọi, chẳng hạn như mã sau, mọi thứ sẽ biên dịch chính xác và hoạt động như trước

    var blah = new Overloaded();
    blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.
    

Đầu tiên, bạn viết hai trường hợp gọi giống nhau. Vì vậy, tất nhiên nó sẽ đi vào cùng một phương pháp, hoặc bạn có ý định viết một cái gì đó khác nhau?
McKay

Nhưng một lần nữa, nếu tôi hiểu chính xác phần còn lại của câu trả lời của bạn, bạn không đọc những gì trình biên dịch nói là sự nhầm lẫn, cụ thể là giữa phương thức thứ nhất và thứ hai, không phải phương thức thứ ba mới mà tôi vừa thêm vào.
McKay

ah, cảm ơn. Nhưng điều đó vẫn để lại vấn đề mà tôi đề cập trong nhận xét thứ hai của tôi cho bài đăng của bạn.
McKay

Để rõ ràng hơn, bạn nêu "Trình biên dịch, hoàn toàn nhầm lẫn giữa phương thức đầu tiên và phương thức này, chỉ cần thêm một phương thức." Nhưng nó không phải. Nó đang gây nhầm lẫn với hai phương pháp còn lại. Phương pháp tham số và phương pháp chung.
McKay

@McKay: mà thôi, chính xác thì thật là nhầm lẫn khi có state3 chức năng, không phải đơn lẻ hay một vài chức năng, chính xác. Trên thực tế, chỉ cần nhận xét bất kỳ ai trong số họ là đủ để giải quyết vấn đề. Kết quả phù hợp nhất giữa các hàm có sẵn là hàm có params, hàm thứ hai là hàm có genericstham số, khi chúng ta thêm hàm thứ ba, nó sẽ tạo ra sự nhầm lẫn trong một settrong các hàm. Tôi nghĩ, rất có thể, đó là thông báo lỗi không rõ ràng do trình biên dịch tạo ra.
Tigran
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.