2012-02-07 32 views
8

Tôi có một ứng dụng .NET có sử dụng một assembly (.dll) định nghĩa một số phương pháp:Tại sao thêm một kiểu trả về một phương pháp trở về khoảng trống gây ra một MissingMethodException

public void DoSomething() 
    { 
     // Do work 
    } 

Giả sử này thay đổi phương pháp chữ ký bao gồm một chuỗi trở lại loại:

public string DoSomething() 
    { 
     // Do work 
     return "something"; 
    } 

Tại sao mã có sử dụng phương pháp này không thành công trên một System.MissingMethodException? Có vẻ như với tôi, ở tất cả các trang web gọi đến phương pháp này, không sử dụng được tạo ra bởi giá trị trả về (vì nó không tồn tại trước đó).

Tại sao thay đổi này phá vỡ mã?

+0

Đã chỉnh sửa ví dụ của tôi. Quan điểm của tôi là phương thức trả về một cái gì đó ngay bây giờ; nhưng tất cả các mã mà trước đó sử dụng nó không làm bất cứ điều gì với nó. Hoàn toàn hợp pháp để viết mã như vậy. –

Trả lời

1

Vì bạn đã thực hiện thay đổi ngắt api. Bạn đã thay đổi chữ ký phương thức trong một lớp cơ sở hoặc một giao diện.

Người gọi được liên kết với phương pháp này. Trong IL một tham chiếu phương thức không chỉ là một tham chiếu đến một kiểu và phương thức của nó với một số chỉ mục cho phương thức nhưng các tham chiếu phương thức của người gọi không bao gồm chữ ký phương thức hoàn chỉnh. Thay đổi này do đó có thể sửa chữa bằng cách biên dịch lại tất cả các assembly gọi phương thức này nhưng bạn nhận được các ngoại lệ thời gian chạy khi bạn chỉ biên dịch lại assembly đã thay đổi và hy vọng các assembly sử dụng sẽ nhận được chữ ký của phương thức đã thay đổi. Đây không phải là trường hợp vì tham chiếu phương thức chứa chữ ký phương thức hoàn chỉnh và kiểu xác định.

Điều đó cũng xảy ra với tôi. Bạn nói đúng là không ai có thể sử dụng kiểu trả về để thay đổi này an toàn nhưng bạn cần phải biên dịch lại tất cả các mục tiêu bị ảnh hưởng.

+0

Cảm ơn. Tôi đã có ấn tượng rằng chỉ có tên phương thức + kiểu tham số được sử dụng để suy ra phương thức nào cần gọi. –

+1

nó thực sự là typeref + methodref trong đó tham chiếu phương thức chứa tên, kiểu trả về, kiểu arguemtns, đối số chung và quy ước gọi –

+0

@liortal: Kiểu trả về không được C# sử dụng để xác định phương thức cần gọi. Nhưng C# chắc chắn cần phải biết kiểu trả về * để tạo mã đúng trong người gọi của phương thức *. –

0

Các nhà sản xuất của C# đã quyết định phải nghiêm ngặt về chữ ký phương thức.

+1

Từ ngoại lệ ('MissingMethodException') Tôi tin rằng anh ta mô tả một lỗi thời gian chạy gây ra bằng cách thay thế dll bằng một cái khác. Nếu điều này là như vậy thì nó không có gì để làm với C#. –

2

Nếu không có phản ánh liên quan nhưng liên kết là tĩnh (tôi giả định điều này từ mô tả) thì thời gian chạy sẽ cố gắng tìm một phương thức sử dụng chữ ký chính xác. Đó chỉ là cách hoạt động của CIL callvirt.

Nó không quan trọng sau đó có hay không giá trị được tiêu thụ - thời gian chạy là không thể xác định vị trí void YourClass::DoSomething() và nó thậm chí không cố gắng tìm kiếm string YourClass::DoSomething().

Nếu thay đổi như vậy có thể xảy ra thì bạn có thể dễ dàng làm tăng thời gian chạy bằng cách gây tràn dòng/tràn ngăn xếp.

+0

+1 Chưa kể rằng bạn sẽ không muốn nó đoán và có lẽ chọn sai phương pháp - có thể khó chịu nhanh chóng ... – Basic

+0

Tôi nghĩ rằng chọn phương pháp chỉ được thực hiện bằng cách nhìn vào tên + tham số của nó. –

+2

Bạn đã nghĩ sai! – adelphus

1

Vì bạn đã thay đổi chữ ký phương thức.

Khi mã bên ngoài cần định vị một phương thức, nó cần phải đảm bảo nó đang gọi là đúng. Nó lưu trữ một số thông tin này như là một chữ ký tại thời gian biên dịch - thông tin chữ ký bao gồm kiểu trả về (bất kể nó có thực sự được sử dụng ở bất kỳ đâu).

Theo như CLR có liên quan, một phương pháp có loại trả lại khoảng trống không còn tồn tại - do đó, MissingMethodException.

+0

Thú vị. Tôi tự hỏi những gì MSIL của trông giống như (trước và sau). –

14

Các câu trả lời khác cho biết bạn đã thay đổi chữ ký của một phương thức và do đó phải biên dịch lại người gọi, là chính xác.Tôi nghĩ tôi có thể thêm một số thông tin bổ sung về câu hỏi này của bạn:

Có vẻ như ở tất cả các trang gọi đến phương thức này, không sử dụng giá trị trả lại (vì nó không tồn tại trước đây).

Điều đó hoàn toàn chính xác. Bây giờ, hãy xem xét câu hỏi này: làm thế nào để bạn viết mã rằng không sử dụng dữ liệu? Bạn dường như lao động theo giả định hoàn toàn sai rằng không sử dụng giá trị không yêu cầu mã, nhưng không sử dụng giá trị chắc chắn yêu cầu mã!

Giả sử bạn có phương pháp:

static int M1(int y) { return y + 1; } 
static void M2(int z) { ... } 

và bạn có một cuộc gọi

int x; 
x = M1(123); 

gì xảy ra ở cấp IL? như sau:

  • Không gian phân bổ trên nhóm tạm thời cho x.
  • Đẩy 123 trên ngăn xếp
  • Gọi M1.
  • Đẩy 1 vào ngăn xếp. Ngăn xếp bây giờ là 1, 123
  • Thêm hai thứ trên cùng vào ngăn xếp. Điều này xuất hiện cả hai và đẩy kết quả. Ngăn xếp tại là 124.
  • Quay lại gọi
  • Các ngăn xếp vẫn là 124.
  • Store giá trị trên stack vào lưu trữ tạm thời cho x. Điều này bật ngăn xếp, do đó, ngăn xếp hiện đang trống.

Giả sử bây giờ bạn làm:

M1(345); 

gì sẽ xảy ra? Cùng một điều:

  • Đẩy 345 trên stack
  • Gọi M1.
  • Đẩy 1 vào ngăn xếp. Ngăn xếp bây giờ là 1, 345
  • Thêm hai thứ trên cùng vào ngăn xếp. Điều này xuất hiện cả hai và đẩy kết quả. Ngăn xếp tại là 346.
  • Quay lại gọi
  • Các ngăn xếp vẫn là 346.

Nhưng không có hướng dẫn mà các cửa hàng giá trị trên stack bất cứ nơi nào, vì vậy chúng ta phải đưa ra một hướng dẫn pop:

  • Bật giá trị chưa sử dụng khỏi ngăn xếp.

Bây giờ giả sử bạn gọi

M2(456); 

gì sẽ xảy ra?

  • Đẩy 456 trên ngăn xếp
  • Gọi M2.
  • M2 thực hiện điều đó. Khi nó trở về người gọi, ngăn xếp trống vì nó không có hiệu lực.
  • Ngăn xếp hiện đang trống, vì vậy đừng bật bất cứ thứ gì.

Bây giờ, bạn có thấy lý do tại sao thay đổi phương thức không có hiệu lực trở về giá trị trả lại là thay đổi không? Mọi người gọi hiện phải bật giá trị không được sử dụng khỏi ngăn xếp. Làm không có gì với dữ liệu vẫn yêu cầu phải làm sạch nó lên ngăn xếp. Bạn đang sắp xếp lại ngăn xếp nếu bạn không bật giá trị đó; CLR yêu cầu ngăn xếp là trống ở đầu mỗi tuyên bố để đảm bảo rằng loại sai lệch này không xảy ra.

+1

+100 Thành tích được mở khóa - (Eric Lippert đã trả lời câu hỏi của bạn) –

+0

Ngăn xếp cân bằng là một điều tốt. Nhưng đôi khi tôi thực sự muốn sao chép một số giá trị từ ngăn xếp của người gọi của tôi đặc biệt là phương pháp xử lý để có được một đi bộ stack rất rẻ của người gọi của tôi. Ở cấp độ IL tôi đã không tìm thấy bất kỳ cách nào để làm điều này. Bạn có biết về khả năng như vậy hay tôi phải nghỉ lại GetStackFramesInternal? –

+0

@AloisKraus ngăn xếp đánh giá IL không giống với ngăn xếp cuộc gọi. Ngăn xếp đánh giá khá ảo. Xem câu trả lời của Eric http://stackoverflow.com/a/7877736/385844 để biết thêm chi tiết. – phoog

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