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

两个关键字Shadows和Overrides的意义是什么? 他们做了什么,为什么上下文是一个或另一个更好?

我不会认为Shadows真的是一个OOP的概念。 覆盖表示您正在为在祖先类中声明的方法/属性等提供新的或附加的function。 阴影真的欺骗编译器认为父方法/属性等甚至不存在。

我对阴影没有用处。 坚持重写。 VB所提供的这些有用的小“特征”types总是会在某个时候导致你的悲伤。

覆盖是更正常的限定符。 如果子类以这种方式重新定义了基类函数,那么无论如何引用子对象(使用基类或子类引用),它都是被调用的子函数。

另一方面,如果子类函数Shadows基类函数,则通过基类引用访问的子对象将使用该基类函数,尽pipe它是一个子对象。
只有在使用匹配的子引用访问子对象时才使用子函数定义。

阴影可能不会做你认为的事情。

考虑以下类:

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这是非常危险的行为,在文档中几乎没有提到。

如果你使用覆盖,他们将是相同的。

所以,虽然有阴影的合法用途,但是你所做的任何事情都不是其中之一,应该避免。

覆盖 – 为方法扩展或创build替代function。

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

 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 

阴影 – 重新定义了一个inheritance的方法,并强制它用于所有类的实例。 换句话说,这个方法没有被重载,但是被重新定义,并且基类方法是不可用的,因此强迫使用在类中声明的函数。 阴影保留或保留方法的定义,以便在基类方法被修改时不被破坏。

例如:强制所有“B”类使用它的古怪添加定义,如果A类添加方法被修改,它不会影响B的添加。 (隐藏所有基类的“Add”方法,不能从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 

有时候一个小例子确实有助于以技术的方式来理解这个区别。

 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 

“阴影”关键字基本上说:“如果谁访问这个对象知道它是这种types或其后代之一,使用这个成员;否则使用基础之一。 这个最简单的例子可能是一个基类ThingFactory,它包含一个返回Thing的“MakeNew”方法和一个从ThingFactory派生的CarFactory类,它的“MakeNew”方法总是返回一个派生types为Car的Thing。 如果一个例程知道它所持有的ThingFactory发生了什么,更具体地说,是一个CarFactory,那么它将使用一个阴影的CarFactory.MakeNew(如果存在),它可以指定返回types为Car。 如果一个例程不知道它的ThingFactory实际上是一个CarFactory,它将使用一个无影子的MakeNew(它应该调用一个内部的受保护的可重写的MakeDerivedThing方法)。

顺便说一下,阴影的另一个好用处是防止派生类访问将不再工作的受保护的方法。 除了分配一个新派生类之外,没有办法从派生类中隐藏一个成员,但是可以通过声明一个新的受保护的空类来防止派生类对被保护成员做任何事情。 例如,如果在对象上调用MemberwiseClone会破坏它,可以声明:

  受保护的阴影类MemberwiseClone
  末class

请注意,这并不违反像Liskovreplace原则这样的OOP原则,因为这只适用于可能使用派生类代替基类对象的情况。 如果Foo和Bar从Bozinheritance,接受Boz参数的方法可以合法地在Foo或Bar中传递。 另一方面,Footypes的对象会知道它的基类对象是Boztypes的。 它永远不会是其他任何东西(例如保证不是酒吧)。

一个阴影的例子:假设你想在第三方组件中使用一个函数,但是函数是受保护的。 你可以用简单的inheritance来绕过这个约束,并且公开一个基本上调用它的基本函数的阴影函数:

 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 

我认为人们在这里采取的确有两种情况,都是合法的。 你真的可以把它们分解成基类devise者和几年后的开发者,他们正在实现不能修改基类的子类。 所以是的,如果你有这种奢侈,最好的办法就是重写。 这是干净的OOD方法。

另一方面,你可能有类似上面给出的例子,在这个方程的另一端必须实现一个子类,而且你不能改变你需要覆盖的方法没有被标记为可覆盖的事实。 举个例子

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

在这种情况下,我从Winform控件类inheritance我的类,不幸的是没有标记为可覆盖。 在这一点上,我只是把代码做成“纯粹的”,或者使它更容易理解。 这个控件的客户端只是想调用control.Focus(),可能不关心。 我可以命名这个方法FocusSearchText()或Focus2等,但我相信上述是更简单的客户端代码。 诚然,如果客户端将这个控件作为基类来调用,并且调用Focus,我的代码将不会执行。 但是这相当遥远。

最后归结为一个判断呼吁,你必须做出一个判断。

这是最近的MSDN链接: 阴影和重写之间的区别

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

**您预计您的基类可能会被修改以定义一个与您的名称相同的元素。

**你想要改变元素types或调用顺序的自由

(我还没有调查范围和types的用法)

影子允许你做某些事情,不能用overrides来完成。

在我自己的情况下:我有几个具有通用function的表类。 但是collections品本身是不同types的。

 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 

我无法重写,因为types已更改。

我发现另一个区别。 看到这个:

 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 

如果使用覆盖(有$$$)如果实例的定义是派生的,并且如果定义是基础的,但实例是派生types,那么在基类上使用Func是没有办法的。

如果你正在使用Shadows,你可以看到Func进入派生类的唯一方法是将实例定义为Derived,而不传递给基类的方法(X.Test返回Standard)。 我认为这是主要的:如果我使用阴影,该方法不会重载基本方法内的基本方法。

这是Overloads的OOP方法。 如果我派生一个类,并在任何情况下我想要一个方法将被称为,我不得不使用超载。 对于我的对象的实例,没有办法返回“标准”(除了使用reflection,我认为)。 我认为intellisense有点混乱。 如果我突出显示Y.Func,会将Func突出显示为基类,但将Func执行为派生类。

使用阴影,新方法只能直接访问。 像重载一样,但是隐藏了基类的重载(我认为这是编译之前返回的错误,因为你可以使用强制types来调用它,比如隐式地使用重载)。

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

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

我想使用System.Web.HttpContext.Current.Response而不是Response.redirect ,并且需要方便的编写Response.redirect 。 我定义了一个名为Response的只读属性,将原始文件映射到基类中。 我不能使用覆盖,因为这个属性是不可覆盖的。 很方便:)

那么这里是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会在没有明确写入阴影修饰符的情况下提示你。

注意:对于我来说,我从来没有使用过一个基类对一个子对象的引用。 对于这种情况我总是使用接口。

阴影可以是非常有用的,如果你正在编写一个现有的控制包装。

例如围绕combobox。 通过对AutoCompleteSource遮蔽,可以防止将其设置为特殊types的combobox的非法值,即使将其转换为正常的combobox也是如此。 或者在影子属性中使用mybase.AutoCompleteSource = value之前做一些预处理。

阴影的使用是罕见的,但真实的。 此外,你不能重写共享(静态)方法。 所以如果你想“覆盖”它,你必须映射一个共享的方法。