答案 0 :(得分:86)
覆盖是更常规的限定符。如果子类以这种方式重新定义基类函数,那么无论如何引用子对象(使用基类或子类引用),它都是被调用的子函数。
另一方面,如果子类函数 Shadows 基类函数,则通过基类引用访问的子对象将使用该基类函数,尽管它是子对象。登记/> 仅当使用匹配的子引用访问子对象时才使用子函数定义。
答案 1 :(得分:19)
暗影可能不会像你想象的那样。
考虑以下课程:
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这是非常危险的行为,在文档中几乎没有提及。如果您使用了覆盖,它们将是相同的。
因此,虽然阴影有合法用途,但你做的任何事情都不是其中之一,应该避免。
答案 2 :(得分:14)
覆盖 - 扩展或创建方法的备用功能。
示例:添加或扩展窗口的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”类使用它的古怪添加定义,如果A类添加方法被修改,它将不会影响B的添加。 (隐藏所有基类“添加”方法。将无法从B的实例调用A.Add(x,y,z)。)
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
答案 3 :(得分:10)
有时,一个小例子确实有助于理解技术方面的差异。
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
答案 4 :(得分: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
答案 5 :(得分:6)
“shadows”关键字基本上表示“如果访问此对象的人知道它属于此类型或其后代之一,请使用此成员;否则使用基本成员。”最简单的例子可能是基类ThingFactory,它包含一个返回Thing的“MakeNew”方法,以及一个派生自ThingFactory的CarFactory类,其“MakeNew”方法总是返回一个派生类型为Car的Thing。如果例程知道它所持有的ThingFactory,更具体地说是CarFactory,那么它将使用阴影CarFactory.MakeNew(如果存在),它可以将返回类型指定为Car。如果例程不知道它的ThingFactory实际上是CarFactory,它将使用非阴影的MakeNew(它应该调用内部受保护的可覆盖的MakeDerivedThing方法)。
顺便说一下,阴影的另一个好处是防止派生类访问不再有效的Protected方法。除了分配新成员之外,没有办法简单地从派生类中隐藏成员,但是可以通过声明具有该名称的新受保护空类来阻止派生类对受保护成员执行任何操作。例如,如果在对象上调用MemberwiseClone会破坏它,可以声明:
Protected Shadows Class MemberwiseClone End Class请注意,这并不违反Liskov替换原则之类的OOP原则,因为这仅适用于可能使用派生类代替基类对象的情况。如果Foo和Bar继承自Boz,则接受Boz参数的方法可以合法地在Foo或Bar中传递。另一方面,类型为Foo的对象将知道其基类对象是Boz类型。它永远不会是其他任何东西(例如它保证不是一个Bar)。
答案 6 :(得分:4)
我认为人们在这里有两种情况,两者都是合法的。您可以真正将它们分解为基类设计器和开发人员多年后实现无法修改基类的子类。所以,是的,如果你有这种奢侈,最好的办法就是覆盖它。这是干净的OOD方法。
另一方面,你可能有类似上面给出的例子,你在这个等式的另一端必须实现一个子类,你不能改变你需要覆盖的方法没有被标记为可覆盖的事实。举个例子
Public Shadows Function Focus() As Boolean
txtSearch.Focus()
Return MyBase.Focus()
End Function
在这种情况下,我从Winform控件类继承我的类,遗憾的是它没有被标记为可覆盖。在这一点上,我面临的只是使代码“纯粹”或使其更容易理解。这个控件的客户端只想调用control.Focus(),可能并不关心。我可以将此方法命名为FocusSearchText()或Focus2等,但我相信上述内容对于客户端代码来说要简单得多。确实,如果客户端然后将此控件转换为基类并调用Focus,则我的代码将不会执行。但那是相当遥远的。
最后,它归结为一个判断电话,你必须做出一个。
答案 7 :(得分:2)
这是最近的MSDN链接: Differences between shadowing and overriding
Shadowing可防止后续的基类修改,该修改引入了您已在派生类中定义的成员。 在以下情况下,您通常使用阴影:
**您预计可能会修改您的基类以使用与您的名称相同的名称定义元素。*
**您希望自由更改元素类型或调用序列。*
(我还没有调查范围和类型的用法)
答案 8 :(得分:2)
Shadow允许您执行某些无法通过覆盖执行的操作。
在我自己的情况下:我有几个具有通用功能的表类;但是对于他们来说,收藏本身是不同类型的。
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
我无法覆盖,因为类型已更改。
答案 9 :(得分:2)
我发现了另一个区别。见:
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(其中有$$$),如果实例的定义是Derived,并且定义是base但实例属于Derived类型,则无法在Base类上使用Func
如果您正在使用Shadows,那么您可以将Func看到派生类的唯一方法是将实例定义为Derived,而不传递给基类的方法(X.Test返回Standard)。我认为这是主要的:如果我使用Shadows,该方法不会重载基本方法中的基本方法。
这是Overloads的OOP方法。如果我派生一个类并且在NO CASE中我想要调用一个方法,我必须使用Overloads。对于我的对象的实例,没有办法返回“标准”(我认为除了使用反射)。我认为intellisense有点混乱。如果我突出显示Y.Func,将突出显示Func到基类,但是将Func强制执行到派生类。
使用Shadows,新方法只能直接访问。比如Overloads,但隐藏了基类的重载(我认为这是在编译之前返回的错误,因为你可以使用强制转换来调用它,例如使用重载隐式完成)。
答案 10 :(得分:1)
这是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会在没有您明确编写阴影修改器的情况下警告阴影。
注意:对我来说,我从未使用过对子对象的Base类引用。对于这种情况,我总是使用接口。
答案 11 :(得分:0)
VB比C#使用更高级的OOP概念,要在C#的派生类中使用重写,必须将方法标记为“ vitrual”,Shadow允许您在没有此条件的情况下进行重载。
阅读有关此this post的MS文档
答案 12 :(得分:0)
阴影的使用很少见,但也是如此。此外,您不能覆盖共享(静态)方法。因此,如果要“覆盖”它,则必须隐藏共享方法。
答案 13 :(得分:0)
如果要在现有控件周围编写包装器,阴影会非常有用。
例如在一个组合框周围。通过遮蔽AutoCompleteSource
,您可以防止将其设置为特殊类型的组合框的非法值,即使它被转换为正常的组合框。或者在阴影属性中使用mybase.AutoCompleteSource = value
之前进行一些预处理。
答案 14 :(得分:0)
我想使用System.Web.HttpContext.Current.Response
代替Response.redirect
,并且需要方便代码为Response.redirect
。我定义了一个名为Response
的只读属性来隐藏基类中的原始属性。我无法使用覆盖,因为此属性不可覆盖。非常方便:)
答案 15 :(得分:0)
我同意吉姆的观点。我也从未找到过Shadows的合法用途。通常,如果我看到它,我认为代码的子部分需要稍微重构。
我想它就在那里,你可以从一个你无法控制源代码的程序集中隐藏一个方法。在这种情况下,重构父类是不可能的。
答案 16 :(得分:-11)
我不认为Shadows真的是一个OOP概念。覆盖表示您正在为祖先类中声明的方法/属性等提供新功能或附加功能。阴影真的欺骗编译器认为父方法/属性等甚至不存在。
我对阴影没用。坚持覆盖。 VB多年来提供的这些有用的小“功能”总是会让你在某些时候感到悲伤。