关于.net:VB.NET中的”阴影”与”替代”

'Shadows' vs. 'Overrides' in VB.NET

两个关键字Shadows和Overrides的意义是什么?它们的作用是什么,哪种情况更适合?


Overrides是更普通的限定词。如果子类以这种方式重新定义了基类函数,则无论如何引用子对象(使用基类或子类引用),都将调用该子函数。

另一方面,如果子类函数对基类函数进行了阴影处理,则尽管是子对象,但通过基类引用访问的子对象仍将使用该基类函数。
仅当使用匹配的子引用访问子对象时,才使用子函数定义。


阴影可能没有按照您的想象做。

请考虑以下类:

1
2
3
4
5
6
7
8
9
10
11
12
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

现在我使用它们:

1
2
3
Dim oA As A
Dim oB As New B
oA = oB

您可能认为oA和oB是对的吗?

不。

oA.fx = 0而oB.fx = 1

恕我直言,这是非常危险的行为,在文档中几乎没有提及。

如果您使用了覆盖,则它们将是相同的。

因此,尽管阴影有合法用途,但您所做的任何事情都不是其中之一,应避免。


覆盖-扩展或创建方法的替代功能。

示例:添加或扩展窗口的Paint事件的功能。

1
2
3
4
    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-重新定义一个继承的方法,并强制将其用于所有用该类型实例化的类。换句话说,该方法未重载,但已重新定义,并且基类方法不可用,因此强制使用该类中声明的函数。 Shadows保留或保留了方法的定义,以便在修改基类方法的情况下不会破坏它。

示例:强制所有" B "类使用它的奇数添加定义,这样,如果修改了A类Add方法,则不会影响B \\的添加。 (隐藏所有基类" Add "方法。将无法从B的实例调用A.Add(x,y,z)。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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


有时候,一个小例子确实可以从技术上帮助理解差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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

阴影的示例:假设您要在第三方组件中使用功能,但该功能受到保护。您可以通过简单的继承绕过此约束,并公开一个阴影函数,该函数基本上调用其基本函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

" shadows "关键字基本上表示"如果访问此对象的人知道它是这种类型或其后代之一,请使用此成员;否则请使用基数。"可能是ThingFactory的基类,其中包括一个返回Thing的" MakeNew "方法,以及一个从ThingFactory派生的CarFactory类,该类的ThingFactory的" MakeNew "方法始终返回一个Thing,该Thing将是Car的派生类型。如果例程知道它拥有的ThingFactory碰巧是CarFactory,则它将使用阴影的CarFactory.MakeNew(如果存在),可以将返回类型指定为Car。如果例程不知道其ThingFactory实际上是CarFactory,它将使用无阴影的MakeNew(应调用内部受保护的可重写MakeDerivedThing方法)。

顺便说一句,shadow的另一个好用法是防止派生类访问不再起作用的Protected方法。除了分配新成员之外,没有其他方法可以简单地从派生类中隐藏成员,但是可以通过使用该名称声明一个新的受保护的空类来防止派生类对受保护成员执行任何操作。例如,如果在一个对象上调用MemberwiseClone会破坏该对象,则可以声明:

1
2
  Protected Shadows Class MemberwiseClone
  End Class

请注意,这不违反Liskov替换原理之类的OOP原理,因为它仅适用于可能使用派生类代替基类对象的情况。如果Foo和Bar从Boz继承,则可以合法地在Foo或Bar中传递接受Boz参数的方法。另一方面,类型为Foo的对象将知道其基类对象为Boz类型。


我认为人们在这里确实有两种情况,并且都是合法的。您真的可以将它们分解为基类设计器,以及几年后实现子类且无法修改基类的开发人员。所以,是的,最好的办法是在有奢侈品的情况下超越。这是干净的OOD方法。

另一方面,您可能会遇到类似上面的示例的情况,其中该方程的另一端必须实现子类,并且您不能更改需要覆盖的方法未标记为可重写的事实。以

为例

1
2
3
4
Public Shadows Function Focus() As Boolean
    txtSearch.Focus()
    Return MyBase.Focus()
End Function

在这种情况下,我将从Winform控件类继承我的类,不幸的是,该类未标记为可重写。在这一点上,我面临的只是编写代码" pure"或使其更易于理解。该控件的客户端只想调用control.Focus(),可能并不在意。我可以将这个方法命名为FocusSearchText()或Focus2等,但是我相信上面的代码对于客户端代码来说要简单得多。的确,如果客户端随后将此控件转换为基类并调用Focus,我的代码将不会执行。但这是很遥远的。

最后要归结为一个判断电话,您必须拨打电话。


这是最近的MSDN链接:
阴影和覆盖

之间的区别

Shadowing可以防止后续的基类修改引入您已在派生类中定义的成员。
通常在以下情况下使用阴影:

**您期望可以修改基类以使用与您的名称相同的名称来定义元素。*

**您希望自由更改元素类型或调用顺序。*

(我尚未调查有关范围和类型的用法)


Shadow允许您执行某些无法通过覆盖完成的事情。

在我自己的情况下:我有几个具有通用功能的表类;但集合本身的类型不同。

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

然后我有一些具体的含义:

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

由于类型已更改,我无法覆盖。


我发现了另一个不同。看到这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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,还是定义是基但实例是Derived类型,都无法在Base类上使用Func。 。

如果使用的是Shadows,则将Func视为派生类的唯一方法是将实例定义为"派生",而无需传递给基类的方法(X.Test返回Standard)。我认为这是主要的:如果我使用Shadows,则该方法不会在基本方法中重载基本方法。

这是"重载"的OOP方法。如果我派生一个类并且在某种情况下我想调用一个方法,则必须使用Overloads。对于我的对象的实例,没有办法返回" Standard "(我认为除了使用反射)。我认为智能感知会造成一些混乱。如果我突出显示Y.Func,则会将Func突出显示为基类,但是将执行Func到派生类。

使用Shadows,只能直接使用新方法。例如Overloads,但是隐藏了基类的重载(我认为这是编译之前返回的错误,因为您可以使用强制转换来调用它,例如使用重载隐式完成)。


这是代码的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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会在没有显式编写阴影修饰符的情况下向您发出警告。

注意:对我来说,我从未使用过对子对象的基类引用。在这种情况下,我总是使用Interfaces。


VB比C#使用更高级的OOP概念,要在C#的派生类中使用重写,必须将方法标记为" vitrual ",Shadow允许您在没有此方法的情况下进行重载。

阅读有关此https://docs.microsoft.com/zh-cn/dotnet/visual-basic/programming-guide/language-features/declared-elements/differences-between-shadowing-and-overriding的MS文档<铅>


我想使用System.Web.HttpContext.Current.Response代替Response.redirect,并且需要方便地将其编码为Response.redirect。我定义了一个名为Response的只读属性,以在基类中隐藏原始属性。我无法使用替代,因为此属性不可替代。很方便:)


如果围绕现有控件编写package器,则阴影可能非常有用。

例如在组合框周围。通过将AutoCompleteSource阴影化,即使将特殊类型的组合框强制转换为普通组合框,也可以防止将其设置为非法值。或者在阴影属性中使用mybase.AutoCompleteSource = value之前进行一些预处理。


使用Shadows很少见,但确实如此。此外,您不能覆盖共享(静态)方法。因此,如果要"覆盖"共享方法,则必须隐藏该共享方法。


我同意吉姆。我也从未找到Shadows的合法用途。通常,如果我看到它,我认为代码的子部分需要重构一点。

我想它在那里,以便您可以从程序集中隐藏一个方法,而您无法控制源代码。在这种情况下,重构父类将是不可能的。


我不会认为Shadows确实是OOP概念。覆盖表示您正在为祖先类中声明的方法/属性等提供新的或其他功能。 Shadows确实诱使编译器认为父方法/属性等根本不存在。

我没用阴影。坚持不懈。 VB多年来提供的这些有用的小"功能"总是会在某些时候使您感到悲伤。