2009-07-28 14 views
14

Czy ktoś może wyjaśnić faktyczne użycie metody ukrywającej się pod numerem w języku C# z ważnym przykładem?metoda ukrywania wC# z prawidłowym przykładem. dlaczego jest wdrażany w ramach? jaka jest zaleta świata rzeczywistego?

Jeśli metoda jest zdefiniowana za pomocą słowa kluczowego new w klasie pochodnej, wówczas nie można jej zastąpić. Następnie jest to to samo, co tworzenie świeżej metody (innej niż wspomniana w klasie bazowej) o innej nazwie.

Czy istnieje jakiś konkretny powód, aby użyć słowa kluczowego new?

+2

Co rozumiesz przez "ważny przykład"? Przykład, który jest po prostu poprawny pod względem składni, lub przykład pokazujący najlepsze praktyki ukrywania metody? –

Odpowiedz

7

C# obsługuje nie tylko przesłonięcie metody, ale także ukrywanie metody. Mówiąc prościej, jeśli metoda nie przesłania metody pochodnej, to ją ukrywa. Metoda ukrywania musi zostać zadeklarowana za pomocą nowego słowa kluczowego. Prawidłowa definicja klasy w drugiej aukcji jest tak:

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

Myślę, że to samo można osiągnąć bez nowego słowa kluczowego, tylko rzecz jest kompilator daje ostrzeżenie. inaczej ma jakąkolwiek istotną różnicę.? – vijaysylvester

+0

nie, nie ma różnicy, kompilator ostrzega i działa tak, jak pisałeś nowe słowo kluczowe, jeśli nie zwracasz uwagi na ostrzeżenie –

+0

Ważne jest, aby typ odwołania określał metodę, która została wywołana. –

10

Jednym z zastosowań czasami mają nowego hasła jest „biednej kowariancji własności Mans” w parallell drzewa dziedziczenia. Rozważmy następujący przykład:

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;}} 
} 

drzewo dziedziczenia Derived1: Podstawa ma 'parallell zależność' na ConcreteDependency1: IDependency”. W klasie pochodnej wiem, że MyDependency jest typu ConcreteDependency1, dlatego mogę ukryć getter właściwości z klasy bazowej za pomocą nowego słowa kluczowego.

EDYCJA: patrz również this blog post by Eric Lippert, aby uzyskać dobre objaśnienie nowego słowa kluczowego.

+3

interesujący -> +1 – Juri

+0

To był naprawdę dobry gość. nie wiedziałem o tych zawiłościach. – vijaysylvester

+0

Uzgodnione, +1 dla pouczającego i użytecznego przykładu. – shambulator

7

Myślę, że przykład ArsenMkrt nie jest w pełni poprawny, a przynajmniej nie wyjaśnia w pełni ukrytej funkcji. Przez upuszczenie nowe słowo kluczowe z metodą Foo w klasie B, będzie wciąż wyjście

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

w języku programowania takich jak Java, gdzie wszystkie metody są „wirtualne”, można się spodziewać, aby uzyskać wyjście

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

poprzez kod ArsenMkrt jest powyżej, ze względu na konkretyzacji

A a; 
B b; 

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

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

w swoim przykładzie jednak jeszcze dostać „a :: Foo()”, ponieważ w C# metody nie są wirtualne domyślnie s o metoda B :: Foo() automatycznie ukrywa A's Foo(). Aby osiągnąć polimorficzne trzeba napisać to w następujący sposób zamiast:.

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

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

Teraz jest gdzie „nowe” słowo kluczowe jest w rzeczywistości po opuszczeniu „override” z B :: Foo(), a następnie znowu ukryłoby A :: Foo(), co oznacza, że ​​nie przesłonisz jego domyślnego zachowania i nie osiągniesz polimorfizmu, tzn. otrzymasz ponownie "A :: Foo()" jako wynik. To samo można osiągnąć - i tutaj nie rozumiem w 100%, dlaczego MUSISZ to ująć - umieszczając słowo kluczowe "nowy", takie jak ..

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

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

i ponownie uzyskać wyjście

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

pytanie to rynek C#, jak widzisz –

+4

Juri podkreśla różnicę z Javą, ale on mówi o C# ... – jeroenh

1

Jeden nieco niejasne scenariusz, w którym metoda ukrywania byłoby właściwe, ale na braku czystej idiomatycznym sposób wyrażania się, to w sytuacji, gdy podstawa klasa ujawnia chronionego członka odziedziczonemu potomkowi, ale ten potomek wie, że nie ma żadnej możliwości, aby jakiekolwiek inne pochodne klasy mogły używać tego członka bez naruszania rzeczy. Najlepszym przykładem metody, którą wiele klas powinno ukrywać (ale bardzo niewielu) jest MemberwiseClone(). W wielu przypadkach, jeśli klasa ma element private, który może być jedynym zachowanym odniesieniem do zmiennego obiektu, nie ma możliwości, aby klasa pochodna mogła poprawnie używać MemberwiseClone().

Należy pamiętać, że ukrywanie chronionych elementów nie stanowi , a nie stanowi naruszenie instrukcji LSP. Zgodność z LSP wymaga, aby w miejscach, w których kod mógłby oczekiwać odniesienia do obiektu klasy bazowej, ale otrzymał odniesienie do obiektu klasy pochodnej, ten ostatni obiekt powinien działać tak samo, jak obiekt bazowy. Członkowie chronieni są jednak poza zakresem LSP, ponieważ każdy typ absolutnie zna swój typ bazowy. Jeśli Bar i Boz obie wywodzą się z Foo i Boz ukrywa element chroniony że Bar wymaga fakt, że Boz ukrywa członek nie wpłynie Bar, ponieważ Bar „s instancja typu podstawa nie może być ewentualnie coś Boz lub inny niż Foo. Żadna substytucja nie jest możliwa, a zatem substytucyjność nie ma znaczenia.

+0

Myślę, że lepiej jest dodać kilka linii kodu do swojej odpowiedzi. Powiedz mi, to lepiej;) – siamak

0

To może pomóc.

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

W powyższym przykładzie deklaracja F w pozycji pochodnej powoduje zgłoszenie ostrzeżenia. Ukrywanie odziedziczonej nazwy nie jest w szczególności błędem, ponieważ wykluczałoby to oddzielną ewolucję klas bazowych. Na przykład powyższa sytuacja mogła się pojawić, ponieważ późniejsza wersja Base wprowadziła metodę F, której nie było we wcześniejszej wersji klasy. Gdyby powyższa sytuacja była błędem, każda zmiana dokonana w klasie bazowej w osobno podzielonej bibliotece klas może potencjalnie spowodować, że klasy pochodne staną się nieważne. Źródło: https://msdn.microsoft.com/en-us/library/aa691135%28v=vs.71%29.aspx

0

Jednorazowy użytek z nich polega na dodaniu/zmodyfikowaniu atrybutu opartego na metodzie bazowej. Na przykład:

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

Podstawa Control klasa ma zdarzenie TextChanged, ale zdarzenie baza zawiera wszystkie rodzaje atrybutów spoliczkował go, aby zapobiec jego pojawienie się w intellisense lub projektanta. Ponieważ klasa, której użyłem, w szerokim zakresie wykorzystuje zarówno właściwość Text, jak i zdarzenie TextChanged, chcę, aby zdarzenie TextChanged pojawiło się w intellisense i było widoczne w oknie właściwości.

Powiązane problemy