2010-07-16 31 views
9

Nếu tôi có một SubOfParent lớp mà là một sub-class của phụ huynh, và hai phương pháp:câu hỏi cơ bản về phương pháp quá tải

public static void doStuff(Parent in) {} 

public static void doStuff(SubOfPArent in) {} 

tại sao đầu tiên doStuff được gọi khi tôi vượt qua một đối tượng kiểu SubOfParent ?

Cảm ơn bạn đã có thông tin chi tiết về điều này!

+0

http://stackoverflow.com/questions/479923/is-ca-single-dispatch-or-multiple-dispatch-language –

+0

Xin vui lòng gửi mã, từ bối cảnh này không ai có ý tưởng – Yakeen

+3

@ Yakeen, có đủ mã ở đây để biết những gì là sai. – tster

Trả lời

12
Parent p = new SubOfParent(); 
SubOfParent s = new SubOfParent(); 

doStuff(p); //calls doStuff(Parent in) 
doStuff(s); //calls doStuff(SubOfParent in) 

//If you're working with a parent but need to call the subclass, you need to upcast it. 
dostuff(p as SubOfParent); 
+0

cảm ơn, điều này hoàn toàn đóng đinh nó –

+4

Rõ ràng trong ví dụ này p là loại SubOfParent, nhưng đó sẽ không phải lúc nào cũng đúng. Để tránh bất kỳ sự ngạc nhiên trong doStuff nó tốt hơn để upcast, kiểm tra null, sau đó thực hiện cuộc gọi. SubOfParent subOfParent = p là SubOfParent; if (subOfParent! = null) doStuff (subOfParent); –

+0

Điều này có ý nghĩa trong một số trường hợp, nhưng khi cố gắng sử dụng quá tải trong một mô hình Chiến lược-ish nó thất bại thảm hại; ví dụ, bạn không thể có mã tự động chọn giữa quá tải SubOfParent và SubOfParent2 cho một biến kiểu Parent được gán một trong các kiểu dẫn xuất. – KeithS

8

quá tải phương thức được thực hiện tại thời gian biên dịch, không phải lúc chạy, vì vậy bạn sẽ không thấy bất kỳ sự đa hình nào với nó. Bất kể mã gọi nào biết loại sẽ được chỉ định phương thức nào được gọi.

nếu bạn muốn gọi phương thức thứ hai bạn có thể cast vào loại SubOfPArent hoặc bạn có thể thay vì đưa các phương pháp đó vào các lớp học và sau đó gọi nó là để có được đa hình:

public class Parent 
{ 
    public virtual void doStuff() {} 
} 

public class SubOfPArent : Parent 
{ 
    public override void doStuff() {} 
} 

...

Parent obj = getASubOfPArent(); 
obj.doStuff(); 
+2

Câu hỏi là về các hàm tĩnh, không phải các hàm mẫu. – apoorv020

+1

@ apoorv020 tster vẫn trả lời đúng câu hỏi. Mặc dù đúng là quá tải phương thức tĩnh được xử lý hơi khác so với các phương thức thể hiện, nhưng nó không tạo ra sự khác biệt trong trường hợp này. –

+1

@ apoorv020, đó là lý do tại sao tôi nói rằng anh ấy có thể di chuyển các chức năng. Rõ ràng tôi khuyên bạn nên thay đổi cách mã được cấu trúc. – tster

4

Có thể bạn đã gọi phương thức có loại biến là Parent.

Vì quá tải phương thức được giải quyết tại thời gian biên dịch, trình biên dịch chỉ có thể chọn một tình trạng quá tải dựa trên các loại thông số thời gian biên dịch tĩnh.

Do đó, mặc dù biến của bạn thực sự có thể chứa một phiên bản SubOfParent khi chạy, trình biên dịch không biết điều đó và do đó sẽ chọn quá tải đầu tiên.

Phương pháp ảo, ngược lại, được giải quyết trong thời gian chạy dựa trên loại thực tế của cá thể có liên quan.
Vì vậy, đã SubOfParent ghi đè một phương pháp ảo, gọi phương thức đó trên một biến loại Parent gọi chính xác phương thức ghi đè nếu trường hợp thực tế là loại SubOfParent.

+0

Tôi đã thông qua SubOfParent - chắc chắn. Phương thức gọi hai phương thức này có số nếu (a.GetType() == typeof (SubOfParent)) { doStuff (a); } else { doStuff (a); } và gỡ lỗi thông qua mã, nó chắc chắn đi qua doStuff đầu tiên (a). –

+0

Ah, nếu bạn phải gõ kiểm tra thì có * thời gian chạy * loại có thể là 'SubOfParent' nhưng tôi nghi ngờ kiểu thời gian biên dịch là' Parent' - trong lựa chọn quá tải C# được định nghĩa tại thời gian biên dịch. –

+0

Đó là sự khác biệt giữa quá tải hàm và nhiều công văn. –

0

Để gọi doStuff (SubOfPArent trong), bạn cần một cái gì đó như thế này:

SubOfPArent.doStuff (SubOfPArent mới());

Nhưng tôi nghĩ bạn không biết loại cho đến khi chạy đúng?

Những gì tster nói là thanh lịch hơn. Tôi nghĩ đó là điều đúng.

0

Về cơ bản, trình biên dịch giải quyết những gì phương pháp được gọi trên một đối tượng (ví dụ, khi đối tượng đó được truyền như một tham số) dựa trên đối tượng mà tuyên bố loại. Vì vậy, nếu bạn có một biến gõ là Parent và bạn vượt qua nó để doStuff, trình biên dịch sẽ giải quyết rằng phương thức gọi là quá tải lấy Parent, ngay cả khi thời gian chạy mà đối tượng hóa ra là SubOfParent.

Phương pháp gọi bởi một đối tượng (ví dụ, phương pháp thể hiện của một lớp) đa hình triển lãm trong thời gian chạy: phương pháp thực hiện được dựa trên đối tượng thực tế loại.

Vì vậy, nếu bạn có điều này:

class Parent 
{ 
    public virtual void doStuff() { } 
} 

class SubOfParent : Parent 
{ 
    public override void doStuff() { } 
} 

Parent p = new SubOfParent(); 
p.doStuff(); 

Sau đó mã sẽ làm việc như bạn mong đợi.

+0

Nếu 'Parent' và' SubOfParent' là (là?) Các lớp do người dùng định nghĩa, có, nó sẽ hoạt động. –

2

Tôi nghĩ bạn đang truyền tới Parent trong mã của mình hoặc cung cấp loại Parent. Vì vậy, tôi nghĩ rằng hành vi là chính xác và những gì bạn đang mong đợi xảy ra là không chính xác.

Đúng là cả hai quá tải đều hợp lệ cho tham chiếu SubOfParent, nhưng logic phân giải quá tải sẽ tìm các phương pháp cụ thể hơn và do đó sẽ tìm thấy quá tải cụ thể nhất.

SubOfParent p = new SubOfParent(); 

doStuff((Parent)p); // Calls base overload 
doStuff(p); // Calls specific overload 
1

Phương pháp quá tải được thực hiện tại thời gian biên dịch và do đó phụ thuộc vào loại tĩnh tại thời gian biên dịch để xác định phương pháp quá tải. Trong ví dụ của bạn, những điều sau có thể xảy ra:

public static void Main(string[] args) 
{ 
    SubOfParent a = new SubOfParent(); 
    doStuff(a); // doStuff(SubOfParent) is called 
} 

Loại tĩnh của thời gian biên dịch là SubOfParent, vì vậy quá tải dự kiến ​​sẽ được gọi.

public static void Main(string[] args) 
{ 
    Parent a = new SubOfParent(); 
    doStuff(a); // doStuff(Parent) is called 
} 

Loại tĩnh (thuộc loại khai báo) là Gốc. Trong trường hợp này, phiên bản quá tải khác sẽ được chọn bất kể giá trị a.GetType() trả về khi chạy.

2

Nếu bạn ok với việc sử dụng các từ khóa dynamic, bạn có thể xem xét việc làm một cái gì đó như thế này:

private static void doStuffImpl(Parent obj) { 
    Console.WriteLine("I'm a Parent!"); 
} 

private static void doStuffImpl(SubOfParent obj) { 
    Console.WriteLine("I'm a Sub of Parent!"); 
} 

public static void doStuff(Parent obj) { 
    try { 
     doStuffImpl(obj as dynamic); 
    } 
    catch (RuntimeBinderException ex) { 
     // Not implemented ! 
    } 
} 

Nó có thể là hữu ích nếu bạn có rất nhiều các lớp học phụ.

doStuffImpl(obj as dynamic); sẽ được đánh giá vào thời gian chạy. doStuffImpl sẽ được gọi với loại thực của obj.

0

bạn có thể quá tải chức năng bằng cách tuyên bố nhiều chức năng với cùng một tên và lập luận khác nhau

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