2009-07-28 18 views
14

Bất cứ ai có thể giải thích việc sử dụng thực tế phương pháp ẩn trong C# với một ví dụ hợp lệ?phương thức ẩn trong C# với một ví dụ hợp lệ. tại sao nó được thực hiện trong khuôn khổ? Lợi thế thế giới thực là gì?

Nếu phương pháp được xác định bằng cách sử dụng từ khóa new trong lớp dẫn xuất, thì nó không thể bị ghi đè. Sau đó, nó cũng giống như việc tạo ra một phương thức mới (khác với phương thức được đề cập trong lớp cơ sở) với một tên khác.

Có lý do cụ thể nào để sử dụng từ khóa new không?

+2

Bạn có ý nghĩa gì với "ví dụ hợp lệ"? Một ví dụ đơn giản là cú pháp chính xác, hoặc một ví dụ cho thấy các phương pháp hay nhất để ẩn phương pháp? –

Trả lời

7

C# không chỉ hỗ trợ ghi đè phương thức, mà còn ẩn phương thức. Nói một cách đơn giản, nếu một phương thức không ghi đè phương thức dẫn xuất, nó sẽ ẩn nó. Phương thức ẩn phải được khai báo bằng từ khóa mới. Định nghĩa lớp học chính xác trong danh sách thứ hai là:

using System; 
    namespace Polymorphism 
    { 
     class A 
     { 
      public void Foo() { Console.WriteLine("A::Foo()"); } 
     } 

     class B : A 
     { 
      public new void Foo() { Console.WriteLine("B::Foo()"); } 
     } 

     class Test 
     { 
      static void Main(string[] args) 
      { 
       A a; 
       B b; 

       a = new A(); 
       b = new B(); 
       a.Foo(); // output --> "A::Foo()" 
       b.Foo(); // output --> "B::Foo()" 

       a = new B(); 
       a.Foo(); // output --> "A::Foo()" 
      } 
     } 
    } 
+1

Tôi đoán tương tự có thể đạt được mà không có từ khóa mới, chỉ có điều là trình biên dịch đưa ra một cảnh báo. nếu không nó có bất kỳ sự khác biệt đáng kể.? – vijaysylvester

+0

không, không có sự khác biệt, trình biên dịch đưa ra cảnh báo và nó hoạt động khi bạn viết từ khóa mới nếu bạn không chú ý đến cảnh báo –

+0

Điều quan trọng ở đây là loại tham chiếu xác định phương thức được gọi. –

10

Một lần tôi sử dụng từ khóa mới cho 'hiệp ước tài sản của người nghèo nghèo' trong cây thừa kế song song. Hãy xem xét ví dụ này:

public interface IDependency 
{ 
} 

public interface ConcreteDependency1 : IDependency 
{ 
} 

public class Base 
{ 
    protected Base(IDependency dependency) 
    { 
    MyDependency = dependency; 
    } 

    protected IDependency MyDependency {get; private set;} 
} 

public class Derived1 : Base // Derived1 depends on ConcreteDependency1 
{ 
    public Derived1(ConcreteDependency1 dependency) : base(dependency) {} 

    // the new keyword allows to define a property in the derived class 
    // that casts the base type to the correct concrete type 
    private new ConcreteDependency1 MyDependency {get {return (ConcreteDependency1)base.MyDependency;}} 
} 

Cây thừa kế Derived1: Base có 'phụ thuộc song song' trên ConcreteDependency1: IDependency '. Trong lớp dẫn xuất, tôi biết rằng MyDependency là kiểu ConcreteDependency1, do đó tôi có thể ẩn thuộc tính getter từ lớp cơ sở bằng cách sử dụng từ khóa mới.

CHỈNH SỬA: xem thêm this blog post by Eric Lippert để có giải thích tốt về từ khóa mới.

+3

thú vị -> +1 – Juri

+0

Đó thực sự là một anh chàng tốt. tôi đã không nhận thức được những người phức tạp – vijaysylvester

+0

Đồng ý, 1 cho ví dụ thông tin và hữu ích. – shambulator

7

Tôi nghĩ ví dụ của ArsenMkrt không hoàn toàn chính xác, ít nhất nó không giải thích đầy đủ tính năng ẩn. Bằng cách thả các từ khóa mới từ phương pháp Foo trong lớp B, bạn vẫn sẽ nhận được đầu ra

A::Foo() 
B::Foo() 
A::Foo() 

Trong một ngôn ngữ lập trình như Java, nơi mà tất cả các phương pháp là "ảo", mà bạn mong đợi để nhận được đầu ra

A::Foo() 
B::Foo() 
B::Foo() 

bằng cách tham gia đang ArsenMkrt của trên, do instantiation

A a; 
B b; 

a = new A(); 
b = new B(); 
a.Foo(); 
b.Foo(); 

a = new B(); //<< Here 
a.Foo(); 

trong ví dụ của mình tuy nhiên, bạn vẫn nhận được "A :: Foo()" bởi vì trong C# phương pháp này không ảo theo mặc định và S o phương thức B :: Foo() tự động ẩn Foo của A(). Để đạt được các hành vi đa hình người ta phải viết nó như sau thay vì:.

class A 
{ 
    public virtual void Foo() { Console.WriteLine("A::Foo()"); } 
} 

class B : A 
{ 
    public override void Foo() { Console.WriteLine("B::Foo()"); } 
} 

Bây giờ là nơi "mới" từ khóa đi kèm trong thực tế khi bạn rời khỏi "override" từ B :: Foo(), sau đó bạn một lần nữa sẽ ẩn A :: Foo() có nghĩa là bạn không ghi đè lên hành vi mặc định của nó và bạn không đạt được đa hình, tức là bạn có được "A :: Foo()" một lần nữa như đầu ra. Điều tương tự cũng có thể đạt được - và đây là nơi tôi không hiểu 100% lý do tại sao bạn phải đặt nó - bằng cách đặt từ khóa "mới" như ..

class A 
{ 
    public virtual void Foo() { Console.WriteLine("A::Foo()"); } 
} 

class B : A 
{ 
    public new void Foo() { Console.WriteLine("B::Foo()"); } 
} 

và bạn lại nhận được đầu ra

A::Foo() 
B::Foo() 
A::Foo() 
+2

câu hỏi là thị trường C# như bạn thấy –

+4

Juri làm nổi bật sự khác biệt với Java, nhưng anh ấy đang nói về C# ... – jeroenh

1

Một kịch bản hơi tối nghĩa mà phương pháp ẩn sẽ là thích hợp, nhưng đối với một thiếu của bất kỳ cách thành ngữ sạch của thể hiện nó, là trong trường hợp cơ sở class cho thấy một thành viên được bảo vệ cho một hậu duệ kế thừa, nhưng hậu duệ đó ​​biết rằng không có cách nào mà các lớp dẫn xuất khác có thể sử dụng thành viên đó mà không phá vỡ mọi thứ. Một ví dụ điển hình của một phương thức mà nhiều lớp nên ẩn (nhưng rất ít) là MemberwiseClone(). Trong nhiều trường hợp, nếu một lớp có một thành viên private mà dự kiến ​​là tham chiếu duy nhất còn tồn tại đối với một đối tượng có thể thay đổi, thì không có phương tiện nào có thể có một lớp dẫn xuất có thể sử dụng chính xác MemberwiseClone().

Lưu ý rằng việc ẩn các thành viên được bảo vệ không không cấu thành vi phạm LSP. Sự phù hợp với LSP yêu cầu ở những nơi mà mã có thể mong đợi tham chiếu đến một đối tượng lớp cơ sở nhưng nhận một tham chiếu đến một đối tượng lớp dẫn xuất, đối tượng thứ hai sẽ hoạt động như một lớp cơ sở. Các thành viên được bảo vệ, tuy nhiên, nằm ngoài phạm vi của LSP, vì mọi loại đều biết loại cơ sở của nó hoàn toàn. Nếu BarBoz cả xuất phát từ Foo, và Boz giấu một thành viên bảo vệ rằng Bar đòi hỏi, thực tế là Boz ẩn thành viên sẽ không ảnh hưởng Bar, bởi vì cơ sở kiểu dụ Bar 's không thể nào là một Boz hoặc bất cứ điều gì khác hơn Foo. Không có sự thay thế nào là có thể, và do đó khả năng thay thế là không liên quan.

+0

tôi nghĩ tốt hơn nên thêm một số dòng mã vào câu trả lời của bạn. Hãy cho tôi, nó tốt hơn;) – siamak

0

Điều này có thể hữu ích.

class Base 
{ 
    public void F() {} 
} 
class Derived: Base 
{ 
    public void F() {}  // Warning, hiding an inherited name 
} 

Trong ví dụ trên, khai báo F có nguồn gốc khiến cảnh báo được báo cáo. Ẩn tên được thừa kế không phải là lỗi, vì điều đó sẽ loại trừ sự tiến hóa riêng biệt của các lớp cơ sở. Ví dụ, tình huống trên có thể xảy ra vì một phiên bản sau của Base đã giới thiệu một phương thức F không có trong phiên bản trước của lớp. Nếu tình huống trên là một lỗi, thì mọi thay đổi được thực hiện cho một lớp cơ sở trong một thư viện lớp được phiên bản riêng biệt có thể khiến các lớp dẫn xuất trở nên không hợp lệ. Nguồn: https://msdn.microsoft.com/en-us/library/aa691135%28v=vs.71%29.aspx

0

Một lần khi bạn sử dụng chúng là khi bạn cần thêm/sửa đổi thuộc tính dựa trên phương pháp lớp cơ sở. Ví dụ:

[Browsable(true)] 
[EditorBrowsable(EditorBrowsableState.Always)] 
public new event EventHandler TextChanged 
{ 
    add { base.TextChanged += value; } 
    remove { base.TextChanged -= value; } 
} 

Các cơ sở Control lớp có một sự kiện TextChanged, nhưng sự kiện cơ sở có tất cả các loại thuộc tính tát vào nó để ngăn chặn nó hiển thị trong IntelliSense hoặc nhà thiết kế. Vì lớp tôi sử dụng để sử dụng rộng rãi cả thuộc tính Text và sự kiện TextChanged, tôi muốn sự kiện TextChanged hiển thị trong nội dung intellisense và hiển thị trong cửa sổ thuộc tính.

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