2015-06-30 26 views
7

chương trình dưới đây tạo ra kết quả này:Tại sao một phương pháp chung được chọn khi không tồn tại chung?

Foo<T> called 

Process is terminated due to StackOverflowException. 

Vì vậy, Foo(baz) gọi chung Foo<T>, nhưng Bar(baz) recurses và làm không gọi Bar<T>.

Tôi đang sử dụng C# 5.0 và Microsoft .NET. Trình biên dịch dường như chọn phương thức chung, thay vì đệ quy, khi phương thức không chung chung là một override.

Tôi có thể tìm giải thích cho quy tắc này ở đâu? (Tôi đã đoán rằng trình biên dịch sẽ chọn đệ quy trong cả hai trường hợp.)

Đây là chương trình trong toàn bộ:

using System; 

namespace ConsoleApplication1 { 
    class Baz { } 

    abstract class Parent { 
     public abstract void Foo(Baz baz); 
    } 

    class Child : Parent { 
     void Bar<T>(T baz) { 
      Console.WriteLine("Bar<T> called"); 
     } 

     public void Bar(Baz baz) { 
      Bar(baz); 
     } 

     void Foo<T>(T baz) { 
      Console.WriteLine("Foo<T> called"); 
     } 

     public override void Foo(Baz baz) { 
      Foo(baz); 
     } 
    } 

    class Program { 
     static void Main(string[] args) { 
      var child = new Child(); 
      child.Foo(null); 
      child.Bar(null); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

Tôi thích xem tất cả các tình huống như thế này theo cách người tạo của trình biên dịch trừng phạt bạn để sử dụng thừa kế ... –

+0

Dường như ưu tiên đang được trao cho phương thức không ghi đè trong lớp con qua phương thức ghi đè trong lớp trẻ; nó có thể điều trị phương pháp ghi đè như là một phần của phụ huynh. – adamdc78

Trả lời

5

Theo các tài liệu MSDN, ưu tiên được dành cho chữ ký phương pháp mà không phải là ghi đè. Vì phiên bản không phổ biến của Foo bị ghi đè, nó ngay lập tức đi đến phần dưới cùng của ưu tiên chọn phương thức. Nói chung, bước tiếp theo là chọn phương thức cụ thể nhất có thể và thực hiện nó. Trong trường hợp của các phương thức Bar, phương pháp Bar(Baz baz) sẽ luôn là phương pháp cụ thể nhất trong trường hợp của bạn.

độ phân giải quá tải là một cơ chế thời gian biên dịch cho việc lựa chọn các thành viên chức năng tốt nhất để gọi đưa ra một danh sách đối số và một bộ thành viên chức năng ứng cử viên. độ phân giải quá tải chọn thành viên chức năng để gọi trong bối cảnh riêng biệt sau đây trong C#:

  • Invocation của một phương thức có tên trong thỉnh nguyện thể hiện (Phần 7.5.5). Gọi một hàm tạo riêng được đặt tên trong một biểu thức tạo đối tượng (Phần 7.5.10.1).
  • Gọi trình truy cập của người lập chỉ mục thông qua truy cập phần tử (Mục 7.5.6). Gọi một toán tử được xác định trước hoặc do người dùng định nghĩa tham chiếu trong một biểu thức (Mục 7.2.3 và Mục 7.2.4).

Mỗi bối cảnh định nghĩa tập của các thành viên chức năng ứng cử viên và danh sách các đối số trong cách độc đáo riêng của nó, như được mô tả chi tiết trong phần liệt kê ở trên. Ví dụ , tập hợp các ứng cử viên cho lời gọi phương thức không bao gồm ghi đè phương pháp (Mục 7.3) và các phương thức trong lớp cơ sở không phải là ứng cử viên nếu có bất kỳ phương pháp nào trong lớp dẫn xuất là áp dụng (Mục 7.5.5.1).

MSDN Overload Resolution

tôi in đậm văn bản mà tôi nghĩ liên quan đến câu hỏi của bạn.

Đây là một câu hỏi khác trên Stack Overflow có thể giúp ích. Nó nói về giải pháp phương pháp nói chung. Không chạm vào các phương pháp ghi đè, nhưng giúp điền vào một số quy trình mà tôi không liên lạc.

+0

tốt đẹp, mà không nhìn vào msdn tôi đã tìm ra lý do! – Fredou

0

có thể nó hoạt động giống như khi bạn thực hiện một cái gì đó như thế này

void myMethod(long? l) { } 
    void myMethod(int? i) { } 

gọi nó với null sẽ sử dụng int?

thêm này một

void myMethod(short? i) { } 

và vẫn gọi nó với null, mã sẽ chuyển sang short?

có thể có một thứ tự nội bộ/ưu tiên được thực hiện?

bây giờ với mã của bạn, tôi đang đưa ra này chỉ để thấy sự khác biệt giữa để cho trình biên dịch quyết định và các lập trình viên quyết định (gọi rõ ràng)

đây là generic Baz

.method private hidebysig 
    instance void Bar<T> (
     !!T baz 
    ) cil managed 
{ 
    // Method begins at RVA 0x2060 
    // Code size 13 (0xd) 
    .maxstack 8 

    IL_0000: nop 
    IL_0001: ldstr "Bar<T> called" 
    IL_0006: call void [mscorlib]System.Console::WriteLine(string) 
    IL_000b: nop 
    IL_000c: ret 
} // end of method Child::Bar 

thực hiện của bạn

public void Bar(Baz baz) { 
     Bar(baz); 
    } 

cho này

.method public hidebysig 
    instance void Bar (
     class ConsoleApplication1.Baz baz 
    ) cil managed 
{ 
    // Method begins at RVA 0x206e 
    // Code size 10 (0xa) 
    .maxstack 8 

    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: call instance void ConsoleApplication1.Child::Bar(class ConsoleApplication1.Baz) 
    IL_0008: nop 
    IL_0009: ret 
} // end of method Child::Bar 

này một

public void Bar(Baz baz) 
    { 
     Bar<Baz>(baz); 
    } 

cho

.method public hidebysig 
    instance void Bar (
     class ConsoleApplication1.Baz baz 
    ) cil managed 
{ 
    // Method begins at RVA 0x206e 
    // Code size 10 (0xa) 
    .maxstack 8 

    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: call instance void ConsoleApplication1.Child::Bar<class ConsoleApplication1.Baz>(!!0) 
    IL_0008: nop 
    IL_0009: ret 
} // end of method Child::Bar 
1

độ phân giải quá tải này tìm kiếm lên chuỗi thừa kế, tìm kiếm các phương pháp định nghĩa tại mỗi điểm.

Child xác định void Foo<T>(T baz) nhưng không xác định void Foo(Baz baz) vì vậy void Foo<T>(T baz) được chọn.

Nói chung điều này có ý nghĩa; trong mã thực nếu Foo<T>(T baz) không làm một công việc rất giống với những gì Foo(Baz baz) trong cơ sở đã làm khi thông qua một Baz, sau đó thiết kế của bạn là khó hiểu và bạn nên chọn một tên mới.

Bạn có thể kludge mọi thứ bằng cách sử dụng public new void Foo(Baz baz) hoặc public new virtual void Foo(Baz baz) để buộc ghi đè được xác định trong Child quá (mặc dù ở đây cần phải là bước trung gian trong phân cấp sao cho phương pháp trừu tượng có triển khai) có thể gọi base.Foo(baz) (để gọi đến triển khai cơ sở) và/hoặc Foo<Baz>(baz) (để gọi vào phiên bản chung) `, nhưng cách tốt nhất nên tránh.

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