«Тени» и «Переопределения» в VB.NET - PullRequest
63 голосов
/ 21 января 2009

Какое значение имеют два ключевых слова Тени и Переопределения ? Что они делают и для какого контекста предпочтительнее тот или иной?

Ответы [ 16 ]

86 голосов
/ 04 февраля 2009

Переопределения - более нормальный квалификатор. Если дочерний класс переопределяет функцию базового класса таким образом, то независимо от того, как на дочерний объект ссылаются (используя базовый класс или ссылку на дочерний класс), вызывается дочерняя функция.

С другой стороны, если функция дочернего класса Shadows функция базового класса, то дочерний объект, доступ к которому осуществляется через ссылку на базовый класс, будет использовать эту функцию базового класса, несмотря на то, что он является дочерним объектом.
Определение дочерней функции используется только в том случае, если доступ к дочернему объекту осуществляется с использованием соответствующей дочерней ссылки.

19 голосов
/ 08 декабря 2010

Слежение, вероятно, не делает то, что вы думаете.

Рассмотрим следующие классы:

Public MustInherit Class A 
    Public Function fX() As Integer
        Return 0
    End Function
End Class

Public Class B
    Inherits A 
    Public Shadows Function fX() As Integer
        Return 1
    End Function 
End Class

Теперь я использую их:

Dim oA As A
Dim oB As New B
oA = oB

Вы, наверное, думаете, что oA и oB одинаковы, верно?

Нет.

oA.fx = 0, а oB.fx = 1

Имхо, это очень опасное поведение, и оно почти не упоминается в документации.

Если бы вы использовали переопределение, они были бы одинаковыми.

Так что, хотя есть и законные способы использования теней, есть вероятность, что то, что вы делаете, не является одним из них, и этого следует избегать.

14 голосов
/ 21 января 2009

Переопределения - расширение или создание альтернативных функций для метода.

Пример: добавление или расширение функциональности события Paint окна.


    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e) ' retain the base class functionality
        'add code for extended functionality here
    End Sub

Shadows - переопределяет унаследованный метод и вынуждает его использовать для всех классов, экземпляры которых имеют этот тип. Другими словами, метод не перегружен, а переопределен, а методы базового класса недоступны, что заставляет использовать функцию, объявленную в классе. Shadows сохраняет или сохраняет определение метода таким образом, чтобы оно не уничтожалось при изменении методов базового класса.

Пример: заставить все классы "B" использовать свое странное определение Add таким образом, чтобы при изменении методов Add класса A это не влияло на добавление B. (Скрывает все методы «Add» базового класса. Не сможет вызвать A.Add (x, y, z) из экземпляра B.)


    Public Class A
        Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
            Return x + y
        End Function
        Public Function Add(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer
            Return x + y + z
        End Function
    End Class
    Public Class B
        Inherits A
        Public Shadows Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
            Return x - y
        End Function
    End Class
10 голосов
/ 22 апреля 2014

Иногда маленький пример действительно помогает понять разницу технически.

Sub Main()

    Dim o As New ChildClass

    Console.WriteLine(o.GetValOverride()) ' Prints 2
    Console.WriteLine(o.GetValShadow()) ' Prints 2
    Console.WriteLine(CType(o, ParentClass).GetValOverride()) ' Prints 2
    Console.WriteLine(CType(o, ParentClass).GetValShadow()) ' Prints 1
    Console.ReadLine()

End Sub

Class ParentClass

    Public Overridable Function GetValOverride() As String
        Return "1"
    End Function

    Public Function GetValShadow() As String
        Return "1"
    End Function

End Class

Class ChildClass
    Inherits ParentClass

    Public Overrides Function GetValOverride() As String
        Return "2"
    End Function

    Public Shadows Function GetValShadow() As String
        Return "2"
    End Function

End Class
6 голосов
/ 20 декабря 2010

Ключевое слово "shadows" по сути говорит: "Если кто-либо обращается к этому объекту, знает, что он принадлежит к этому типу или одному из его потомков, используйте этот элемент; в противном случае используйте базовый". Простейшим примером этого может быть базовый класс ThingFactory, который включает метод «MakeNew», который возвращает Thing, и класс CarFactory, производный от ThingFactory, чей метод «MakeNew» всегда возвращает Thing, который будет иметь производный тип Car. Если подпрограмма знает, что ThingFactory, которой она владеет, происходит, в частности, CarFactory, то она будет использовать затененный CarFactory.MakeNew (если он существует), который может указать тип возвращаемого значения как Car. Если подпрограмма не знает, что ее ThingFactory на самом деле является CarFactory, она будет использовать не затененный MakeNew (который должен вызывать внутренний защищенный переопределяемый метод MakeDerivedThing).

Кстати, еще одно хорошее использование теней - запретить доступ производных классов к защищенным методам, которые больше не будут работать. Невозможно просто скрыть член от производных классов, кроме назначения нового, но можно запретить производным классам делать что-либо с защищенным членом, объявив новый защищенный пустой класс с этим именем. Например, если вызов MemberwiseClone для объекта сломает его, можно объявить:

  Protected Shadows Class MemberwiseClone
  End Class
Обратите внимание, что это не нарушает принципы ООП, такие как принцип замещения Лискова, поскольку это применимо только в тех случаях, когда производный класс может использоваться вместо объекта базового класса. Если Foo и Bar наследуются от Boz, метод, который принимает параметр Boz, может быть законно передан в Foo или Bar. С другой стороны, объект типа Foo будет знать, что его объект базового класса имеет тип Boz. Это никогда не будет чем-то еще (например, это гарантированно не будет Баром).
6 голосов
/ 24 сентября 2009

Пример затенения: предположим, что вы хотите использовать функцию в стороннем компоненте, но функция защищена. Вы можете обойти это ограничение с помощью простого наследования и предоставления скрытой функции, которая в основном вызывает ее базовую функцию:

Public Class Base

    Protected Sub Configure()
        ....
    End Sub

End Class

Public Class Inherited
    Inherits Base

    Public Shadows Sub Configure()
        MyBase.Configure()
    End Sub

End Class
4 голосов
/ 30 сентября 2011

Я думаю, что на самом деле есть два сценария, которые люди принимают здесь, и оба являются законными. Вы могли бы по-настоящему разбить их на дизайнера базового класса и разработчика спустя годы, который реализует подкласс, который не может модифицировать базовый класс. Так что да, лучшее, что нужно сделать, это переопределить, если у вас есть такая роскошь. Это чистый подход OOD.

С другой стороны, у вас может быть что-то похожее на пример, приведенный выше, где вы на другом конце этого уравнения должны реализовать подкласс, и вы не можете изменить тот факт, что метод, который нужно переопределить, не помечен как перезаписываемый. Взять например

Public Shadows Function Focus() As Boolean
    txtSearch.Focus()
    Return MyBase.Focus()
End Function

В этом случае я наследую свой класс от класса управления Winform, который, к сожалению, не помечен как перезаписываемый. На данный момент я сталкиваюсь с тем, чтобы сделать код «чистым» или сделать его более понятным. Клиент этого элемента управления просто хочет вызвать control.Focus () и, возможно, ему все равно. Я мог бы назвать этот метод FocusSearchText () или Focus2 и т. Д., Но я считаю, что вышеизложенное намного проще для клиентского кода. Это правда, что если клиент затем использует этот элемент управления в качестве базового класса и вызывает Focus, мой код не будет оправдан. Но это довольно далеко.

В конце концов, все сводится к судебному вызову, и вам придется его сделать.

2 голосов
/ 26 января 2015

Я нашел другую разницу. Смотрите это:

Sub Main()
    Dim X As New Derived
    Dim Y As Base = New Derived
    Console.WriteLine("X:" & X.Test())
    Console.WriteLine("Y:" & Y.Test())
    Console.WriteLine("X:" & CType(X, Base).Test)
    Console.WriteLine("X:" & X.Func())
    Console.WriteLine("Y:" & Y.Func())
    Console.WriteLine("X:" & CType(X, Base).Func)
    Console.ReadKey()
End Sub
Public Class Base
    Public Overridable Function Func() As String
        Return "Standard"
    End Function
    Function Test() As String
        Return Me.Func()
    End Function
End Class
Public Class Derived
    Inherits Base
    Public $$$ Function Func() As String
        Return "Passed By Class1" & " - " & MyBase.Func
    End Function
End Class

Если вы используете переопределения (где есть $$$), НЕТ ПУТИ использовать Func для базового класса, либо если определение экземпляра является производным, и если определение является базовым, но экземпляр имеет тип производного .

Если вы используете Shadows, единственный способ увидеть Func в производном классе - это определить экземпляр как Derived, и без передачи в метод базового класса (X.Test возвращает Standard). Я думаю, что это главное: если я использую Shadows, метод не будет перегружать базовый метод внутри базовых методов.

Это ООП подход перегрузок. Если я получаю класс и ни в коем случае не хочу вызывать метод, я должен использовать перегрузки. Для экземпляров моих объектов нет способа вернуть «Стандарт» (думаю, кроме использования отражений). Я думаю, что intellisense сделать немного путаницы. Если я выделю Y.Func, то будет выделен Func в базовый класс, но он будет выполнен в производном классе.

С Тенями, новый метод доступен только напрямую. Например, перегрузки, но скрытие перегрузок базового класса (я думаю, что это ошибка, возвращаемая до компиляции, потому что вы можете вызывать ее с помощью приведения, как, например, неявное выполнение с использованием перегрузки).

2 голосов
/ 04 октября 2012

Тень позволяет вам делать определенные вещи, которые нельзя сделать с переопределениями.

В моем собственном случае: у меня есть несколько классов таблиц с общей функциональностью; но для которых сами коллекции бывают разных типов.

Public Class GenericTable
Protected Friend Overridable Property Contents As System.Collections.Generic.List(Of GenericItem)
    ... do stuff ...
End Class

Тогда у меня есть конкретные значения:

Public Class WidgetTable
Inherits GenericTable
Protected Friend Shadows Property Contents As System.Collections.Generic.List(Of Widget)
    ... stuff is inhereted ...
End Class

Не удалось переопределить, так как тип изменился.

2 голосов
/ 03 декабря 2009

Это недавняя ссылка MSDN: Различия между затенением и переопределением

Затенение защищает от последующей модификации базового класса, которая вводит члена, который вы уже определили в своем производном классе. Обычно вы используете теневое копирование в следующих случаях:

** Вы ожидаете, что ваш базовый класс может быть изменен для определения элемента с тем же именем, что и у вас. *

** Вам нужна свобода изменения типа элемента или последовательности вызовов. *

(мне еще предстоит исследовать использование в отношении объема и типов)

...