"Тени" против "Переопределения" в VB.NET

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

Ответ 1

Я бы не считал Shadows действительно концепцией ООП. Переопределения указывают, что вы предоставляете новые или дополнительные функции для метода/свойства и т.д., Которые были объявлены в классе предков. Тени действительно обманывают компилятор, думая, что родительский метод/свойство и т.д. Даже не существует.

Мне не нужно использовать Shadows. Придерживайтесь переопределений. Эти типы полезных "особенностей", которые VB предоставляет годами, в конечном итоге вызывают у вас печаль в какой-то момент.

Ответ 2

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

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

Ответ 3

Тень, вероятно, не делает то, что вы думаете.

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

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

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

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

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

Ответ 4

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

Пример: добавление или расширение функциональности события 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

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

Пример. Принудите все классы "B" использовать его oddball. Добавьте определение так, что если методы добавления класса класса будут изменены, это не повлияет на 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

Ответ 5

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

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

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

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

В конце концов, это сводится к решению суда, и вы должны будете сделать.

Ответ 9

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

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

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

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

(Я еще не исследовал использование в отношении областей и типов)

Ответ 10

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

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

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

Я не мог переопределить, потому что тип был изменен.

Ответ 11

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

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

Если вы используете Overrides (где есть $$$), то нет возможности использовать класс Func on Base, если определение экземпляра Derived и если определение является базовым, но экземпляр имеет тип Derived.

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

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

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

Ответ 12

Я согласен с Джимом. Я никогда не нашел законного использования для Shadows. Обычно, если я это вижу, я предполагаю, что часть кода должна быть реорганизована немного.

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

Ответ 13

Я хотел использовать System.Web.HttpContext.Current.Response вместо Response.redirect и нуждался в удобстве для кода как Response.redirect. Я определил свойство readonly с именем Response, чтобы затенять оригинал в базовом классе. Я не мог использовать переопределения, поскольку это свойство не является переопределяемым. Очень удобно:)

Ответ 14

Ну вот ответ на Code.

Module Module1

    Sub Main()
        Dim object1 As Parent = New Child()
        Console.WriteLine("object1, reference type Parent and object type Child")
        object1.TryMe1()
        object1.TryMe2()
        object1.TryMe3()

        Console.WriteLine("")
        Console.WriteLine("")
        Console.WriteLine("object2, reference type Child and object type Child")
        Dim object2 As Child = New Child()

        object2.TryMe1()
        object2.TryMe2()
        object2.TryMe3()

        Console.ReadLine()
    End Sub

End Module

Public Class Parent

    Public Sub TryMe1()
        Console.WriteLine("Testing Shadow: Parent.WriteMe1")
    End Sub

    Public Overridable Sub TryMe2()
        Console.WriteLine("Testing override: Parent.WriteMe2")
    End Sub

    Public Sub TryMe3()
        Console.WriteLine("Testing Shadow without explicitly writing shadow modifier: Parent.WriteMe3")
    End Sub
End Class

Public Class Child
    Inherits Parent

    Public Shadows Sub TryMe1()
        Console.WriteLine("Testing Shadow: Child.WriteMe1")
    End Sub

    Public Overrides Sub TryMe2()
        Console.WriteLine("Testing override: Child.WriteMe2")
    End Sub

    Public Sub TryMe3()
    Console.WriteLine("Testing Shadow without explicitly writing shadow modifier: Child.WriteMe3")
    End Sub
End Class


'Output:
'object1, reference type Parent and object type Child
'Testing Shadow: Parent.WriteMe1
'Testing override: Child.WriteMe2
'Testing Shadow without explicitly writing shadow modifier: Parent.WriteMe3


'object2, reference type Child and object type Child
'Testing Shadow: Child.WriteMe1
'Testing override: Child.WriteMe2
'Testing Shadow without explicitly writing shadow modifier: Child.WriteMe3

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

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

Ответ 15

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

Например, рядом со списком. Затеняя AutoCompleteSource, вы можете помешать ему установить незаконное значение для вашего специального вида combobox, даже когда оно будет передано в обычное поле со списком. Или выполните предварительную обработку, прежде чем использовать mybase.AutoCompleteSource = value в свойстве shadowing.

Ответ 16

Использование Shadows редко, но верно. Кроме того, вы не можете переопределить общий (статический) метод. Поэтому вы должны затенять общий метод, если хотите "переопределить" его.