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