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



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

821   16  

16 ответов:

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

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

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

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

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

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

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

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

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

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

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

пример: добавление или расширение функциональности события 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-переопределяет унаследованный метод и заставляет его использовать для всех классов, созданных с этим типом. Другими словами, метод не перегружен, а переопределен, и методы базового класса недоступны, что вынуждает использовать функцию, объявленную в классе. Тени сохраняет или сохраняет определение метода таково, что он не уничтожается при изменении методов базового класса.

пример: заставьте все классы " 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* * вы хотите свободы изменение типа элемента или последовательности вызовов.*

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

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

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

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

тогда у меня есть конкретные isntances:

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

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

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

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 в производном классе - определить экземпляр как производный и не переходить к методу базового класса (X. test возвращает Стандарт.) Я думаю, что это главное: если я использую тени, метод не будет перегружать базовый метод внутри базовых методов.

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

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

Ну вот ответ по коду.

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 предупреждает вас, когда затенение происходит без явного написания модификатора тени.

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

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

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

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

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

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

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

Comments

    Ничего не найдено.