VB.NET中的“阴影”与“覆盖”

时间:2009-01-20 21:32:07

标签: .net vb.net oop override shadows

两个关键字ShadowsOverrides的重要性是什么?他们做了什么以及哪个环境是一个或另一个更好?

17 个答案:

答案 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多年来提供的这些有用的小“功能”总是会让你在某些时候感到悲伤。