2009-04-05 30 views
30

Nói rằng tôi có hai phiên bản quá tải của một phương pháp C#:C#: Truyền null đến phương thức quá tải - phương thức nào được gọi?

void Method(TypeA a) { } 
void Method(TypeB b) { } 

tôi gọi là phương pháp với:

Method(null); 

nào tình trạng quá tải của phương pháp này được gọi là? Tôi có thể làm gì để đảm bảo rằng một tình trạng quá tải cụ thể được gọi?

+0

Một số khác phải nhận thức được quá tải phương pháp mà ít nhất một phương pháp sử dụng từ khóa params. –

Trả lời

67

Tùy thuộc vào TypeATypeB.

  • Nếu chính xác một trong số họ được áp dụng (ví dụ không có chuyển đổi từ null để TypeB bởi vì nó là một loại giá trị nhưng TypeA là một loại tài liệu tham khảo) sau đó cuộc gọi sẽ được thực hiện cho một áp dụng.
  • Nếu không, nó phụ thuộc vào mối quan hệ giữa TypeATypeB.
    • Nếu có một chuyển đổi ngầm TypeA-TypeB nhưng không có chuyển đổi ngầm TypeB-TypeA thì tình trạng quá tải sử dụng TypeA sẽ được sử dụng.
    • Nếu có chuyển đổi ẩn từ TypeB thành TypeA nhưng không chuyển đổi ẩn từ TypeA thành TypeB thì sử dụng quá tải TypeB sẽ được sử dụng.
    • Nếu không, cuộc gọi không rõ ràng và sẽ không biên dịch được.

Xem phần 7.4.3.4 của thông số C# 3.0 để biết các quy tắc chi tiết.

Đây là một ví dụ về nó không rõ ràng. Tại đây, TypeB có nguồn gốc từ TypeA, có nghĩa là có chuyển đổi tiềm ẩn từ TypeB đến TypeA, nhưng không phải ngược lại. Do đó, tình trạng quá tải sử dụng TypeB được sử dụng:

using System; 

class TypeA {} 
class TypeB : TypeA {} 

class Program 
{ 
    static void Foo(TypeA x) 
    { 
     Console.WriteLine("Foo(TypeA)"); 
    } 

    static void Foo(TypeB x) 
    { 
     Console.WriteLine("Foo(TypeB)"); 
    } 

    static void Main() 
    { 
     Foo(null); // Prints Foo(TypeB) 
    } 
} 

Nói chung, ngay cả khi đối mặt với một cuộc gọi khác-mơ hồ, để đảm bảo rằng một tình trạng quá tải đặc biệt được sử dụng, chỉ cần cast:

Foo((TypeA) null); 

hoặc

Foo((TypeB) null); 

Lưu ý rằng nếu điều này liên quan đến thừa kế trong các lớp khai báo (tức là một lớp đang quá tải phương thức được khai báo bởi lớp cơ sở), bạn sẽ gặp một vấn đề khác, và bạn ed để bỏ mục tiêu của phương thức chứ không phải đối số.

+0

@Jon: Tại sao hành vi như bạn mô tả trong trường hợp chuyển đổi tiềm ẩn có sẵn? Tôi thà mong đợi điều ngược lại. Nó chỉ là vấn đề định nghĩa? –

+2

@ divo: Đó chắc chắn là một quyết định thiết kế, nhưng có lẽ lý do đằng sau nó là phương pháp TypeB là khá nhiều trường hợp đặc biệt của phương pháp TypeA. Ý tôi là, đối với mọi arg hợp lệ, theo TypeA mặc định sẽ được khớp trừ khi đối tượng được biết là TypeB. Hành vi này đối với null đảm bảo tính nhất quán này. –

+0

Mehrdad là đúng - nếu bạn có thể chuyển đổi từ TypeB thành TypeA, thì TypeB cụ thể hơn TypeA, do đó sẽ được chọn theo sở thích. –

1

gọi mơ hồ. (lỗi thời gian biên dịch).

+0

hi, Tôi đã thử mẫu sau và không nhận được lỗi biên dịch; bất kỳ suy nghĩ tại sao? Chương trình lớp { void test1 (ICollection a) { Console.WriteLine ("test1-ICollection"); } void test1 (IList a) { Console.WriteLine ("test1-List"); } khoảng trống tĩnh Main (string [] args) { Chương trình p = new Program(); p.test1 (null); Console.ReadKey(); }} –

+0

nhưng tôi đã biên dịch báo lỗi khi tôi đã thay đổi một trong những định nghĩa chồng chấp nhận một tham số của kiểu IComparer class Program { trống test1 (IList a) { Console.WriteLine ("test1- Danh sách"); } void test1 (IComparer a) { Bàn điều khiển.WriteLine ("test1-IComparer"); } khoảng trống tĩnh Main (string [] args) { Chương trình p = new Program(); p.test1 (null); Console.ReadKey(); } } –

8

Jon Skeet đã đưa ra một câu trả lời toàn diện, nhưng từ quan điểm thiết kế, bạn không nên phụ thuộc vào các trường hợp góc của đặc tả trình biên dịch. Nếu không có gì khác, nếu bạn phải tìm kiếm những gì nó làm trước khi bạn viết nó, người tiếp theo để cố gắng đọc nó sẽ không biết nó làm gì. Nó không thể duy trì được.

Quá tải có sẵn để thuận tiện và hai trường hợp quá tải khác nhau có cùng tên phải thực hiện tương tự. Nếu hai phương thức làm những việc khác nhau, hãy đổi tên một hoặc cả hai phương thức.

Thông thường, phương pháp quá tải có các biến thể với số lượng tham số khác nhau và cho quá tải với ít tham số hơn để cung cấp mặc định hợp lý.

ví dụ: string ToString(string format, System.IFormatProvider provider) có các thông số nhất,
string ToString(System.IFormatProvider provider) cung cấp một định dạng mặc định, và
string ToString() cung cấp một định dạng mặc định và cung cấp dịch vụ,

+0

Tôi không thấy điểm của bạn về tình trạng quá tải. Đúng, quá tải nên làm điều tương tự, nhưng có thể làm điều đó theo những cách khác nhau. Đó là lý do tại sao bạn muốn quá tải. Số lượng đối số không liên quan và có thể giống hoặc khác nhau (trường hợp tại điểm: các phương thức Chuyển đổi khác nhau). Về trình biên dịch spesifics: tùy thuộc vào mức độ kỹ năng của người tiếp theo, bất cứ điều gì có thể là một «góc trường hợp». Miễn là thiết kế của bạn có ý nghĩa, và hoạt động theo cách trực quan, nó thực hiện công việc. Điều đó có thể yêu cầu xem xét cẩn thận thời gian thiết kế, không quá nhiều khi lớp được sử dụng sau này. –

+0

Tôi nghĩ điểm mà anh ấy tạo ra là việc sử dụng quá tải điển hình nhất là thực hiện một số chức năng trên dữ liệu có thể bao gồm các loại khác nhau hoặc có thể bao gồm nhiều hoặc ít thông số hơn. Trong thực tế, tôi đã viết một số ít các tình trạng quá tải trong đó người có ít thông số hơn thực sự có tất cả cùng một dữ liệu (chuỗi) trong các phần khác nhau ít hơn. Trong trường hợp đó họ gọi là quá tải cao hơn sau khi phá vỡ các mảnh ngoài và cung cấp cho họ một lần nữa. –

0

Một giải pháp đơn giản là tạo ra một phương pháp khác với chữ ký của:

void Method() { } 

hoặc tốt hơn để cập nhật chữ ký trên một trong các phương thức sau:

void Method(TypeB b = null) { } 

và sau đó gọi cho tôi t như vậy:

Method(); 

Tại thời gian biên dịch giá trị null là untyped và do đó trình biên dịch không thể phù hợp với nó lên với một trong những chữ ký phương pháp. Vào thời gian chạy, bất kỳ biến nào có thể giải quyết thành null sẽ vẫn được nhập và do đó nó sẽ không gây ra sự cố.

3

Jon Skeet đã trả lời quá tải nào được chọn theo mặc định, nhưng nếu bạn muốn đảm bảo rằng quá tải cụ thể được gọi, thì tốt hơn nên sử dụng các tham số có tên hơn là truyền.

Nếu bạn có:

void Method(TypeA a) { } 
void Method(TypeB b) { } 

Bạn có thể gọi Method(a: null); hoặc Method(b: null);

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