2つのキーワードShadowsとOverridesの重要性は何ですか?彼らは何をし、どちらの文脈でどちらが好ましいのでしょうか?
17 に答える
Overridesはより一般的な修飾子です。子クラスがこの方法で基本クラス関数を再定義する場合、子オブジェクトがどのように参照されるかに関係なく (基本クラスまたは子クラス参照を使用して)、呼び出されるのは子関数です。
一方、子クラス関数が基本クラス関数をシャドウする場合、基本クラス参照を介してアクセスされる子オブジェクトは、子オブジェクトであるにもかかわらず、その基本クラス関数を使用します。
子関数定義は、一致する子参照を使用して子オブジェクトにアクセスする場合にのみ使用されます。
シャドーイングは、おそらくあなたが思っていることをしません。
次のクラスを検討してください。
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
これは非常に危険な動作であり、ドキュメントではほとんど言及されていません。
オーバーライドを使用した場合、それらは同じになります。
したがって、シャドウには正当な用途がありますが、あなたがしていることはそれらの 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 Sub
シャドウ - 継承されたメソッドを再定義し、その型でインスタンス化されたすべてのクラスにその使用を強制します。つまり、メソッドはオーバーロードされていませんが、再定義されており、基本クラスのメソッドは使用できないため、クラスで宣言された関数を使用する必要があります。Shadows は、基本クラスのメソッドが変更されても破棄されないように、メソッドの定義を保存または保持します。
例: すべての「B」クラスに、そのオッドボールを使用するように強制します。A クラスの Add メソッドが変更されても、B の add に影響しないように、定義を追加します。(基本クラスの「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
シャドウイングの例:サードパーティコンポーネントで関数を使用したいが、関数は保護されていると仮定します。単純な継承と、基本的にその基本関数を呼び出すシャドウ関数を公開することで、この制約を回避できます。
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」キーワードは基本的に、「このオブジェクトにアクセスしている人が、それがこのタイプまたはその子孫の 1 つであることがわかっている場合は、このメンバーを使用し、それ以外の場合はベース メンバーを使用する」ことを意味します。この最も単純な例は、Thing を返す「MakeNew」メソッドを含む基本クラス ThingFactory と、ThingFactory から派生したクラス CarFactory であり、その「MakeNew」メソッドは常に Car の派生型になる Thing を返します。保持している ThingFactory がたまたま CarFactory であることをルーチンが認識している場合、シャドウ化された CarFactory.MakeNew (存在する場合) を使用し、戻り値の型を Car として指定できます。ThingFactory が実際には CarFactory であることをルーチンが認識していない場合、
ちなみに、シャドウのもう 1 つの有効な使い方は、派生クラスが、もはや機能しない Protected メソッドにアクセスするのを防ぐことです。新しいメンバーを割り当てる以外に、派生クラスからメンバーを単純に隠す方法はありませんが、新しい保護された空のクラスをその名前で宣言することにより、派生クラスが保護されたメンバーで何かを行うのを防ぐことができます。たとえば、オブジェクトで MemberwiseClone を呼び出すとオブジェクトが破損する場合は、次のように宣言できます。
Protected Shadows クラス MemberwiseClone クラス終了これは、Liskov Substitution Principle のような OOP 原則に違反しないことに注意してください。これは、基底クラス オブジェクトの代わりに派生クラスが使用される可能性がある場合にのみ適用されるためです。Foo および Bar が Boz から継承する場合、Boz パラメーターを受け入れるメソッドは、代わりに Foo または Bar で正当に渡される可能性があります。一方、タイプ Foo のオブジェクトは、その基本クラスのオブジェクトがタイプ Boz であることを認識します。他のものになることはありません (たとえば、Bar ではないことが保証されています)。
人々がここで取り組んでいるシナリオは実際には 2 つあると思いますが、どちらも正当なものです。それらを基本クラスの設計者と、数年後に基本クラスを変更できないサブクラスを実装する開発者に分けることができます。そうです、その余裕がある場合はオーバーライドするのが最善の方法です。これはクリーンな OOD アプローチです。
一方、サブクラスを実装する必要があるこの方程式の反対側にある上記の例のようなものがあり、オーバーライドする必要があるメソッドがオーバーライド可能とマークされていないという事実を変更することはできません。たとえば、
Public Shadows Function Focus() As Boolean
txtSearch.Focus()
Return MyBase.Focus()
End Function
この場合、残念ながらオーバーライド可能としてマークされていない Winform コントロール クラスからクラスを継承しています。この時点で、コードを「純粋」にするか、理解しやすくするかだけに直面しています。このコントロールのクライアントは単に control.Focus() を呼び出したいだけで、おそらく気にしません。このメソッドに FocusSearchText() や Focus2 などの名前を付けることもできましたが、クライアント コードについては上記の方がはるかに簡単だと思います。クライアントがこのコントロールを基本クラスとしてキャストし、Focus を呼び出すと、コードが実行されないことは事実です。しかし、それはかなり遠いです。
最終的には判断が下され、それはあなたがしなければならないことです。
これは最近の MSDN リンクです: シャドーイングとオーバーライドの違い
シャドウイングは、派生クラスで既に定義したメンバーを導入する後続の基本クラスの変更から保護します。通常、シャドウイングは次の場合に使用します。
** 基本クラスが変更されて、自分の名前と同じ名前を使用する要素を定義する可能性があると予想されます。*
** 要素の型や呼び出しシーケンスを自由に変更したい。
(スコープとタイプに関する使用法はまだ調査していません)
さて、これがコードによる答えです。
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 クラス参照を使用したことがありません。そのような場合、私は常にインターフェースを使用します。
もう一つ違いを見つけました。これを参照してください:
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 を派生クラスに表示する唯一の方法は、インスタンスを Derived として定義し、基本クラスのメソッドに渡さないことです (X.Test は Standard を返します)。これが主なものだと思います。シャドウを使用すると、メソッドは基本メソッド内の基本メソッドをオーバーロードしません。
これがオーバーロードの OOP アプローチです。クラスを派生させ、メソッドが呼び出されることを望んでいない場合は、オーバーロードを使用する必要があります。私のオブジェクトのインスタンスの場合、「標準」を返す方法はありません(反射を使用する場合を除いて)。インテリセンスは少し混乱すると思います。Y.Func を強調表示すると、基本クラスに Func が強調表示されますが、派生クラスに Func が実行されます。
Shadows を使用すると、新しいメソッドに直接到達できます。オーバーロードなどですが、基本クラスのオーバーロードを非表示にします(オーバーロードを使用して暗黙的に行われるように、キャストを使用して呼び出すことができるため、コンパイル前に返されるエラーだと思います)。
シャドウを使用すると、オーバーライドでは実行できない特定のことを実行できます。
私自身の場合: 一般的な機能を持ついくつかのテーブル クラスがあります。しかし、コレクション自体のタイプが異なる人。
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
型が変わったのでオーバーライドできませんでした。
シャドウの使用はまれですが、本当です。さらに、共有 (静的) メソッドをオーバーライドすることはできません。したがって、共有メソッドを「オーバーライド」する場合は、そのメソッドをシャドーする必要があります。
私はジムに同意します。シャドウの正当な用途も見つけられませんでした。通常、私がそれを見ると、コードのサブセクションを少しリファクタリングする必要があると思います。
ソースコードを制御できないアセンブリからメソッドをシャドウできるようにするために、そこにあると思います。その場合、親クラスのリファクタリングは不可能です。
System.Web.HttpContext.Current.Response
の代わりに使用したかったのでResponse.redirect
、 としてコーディングする利便性が必要でしたResponse.redirect
。Response
基本クラスでオリジナルをシャドウするという名前の読み取り専用プロパティを定義しました。このプロパティはオーバーライドできないため、オーバーライドを使用できませんでした。とても便利:)
Shadowsが実際にOOPの概念であるとは思いません。オーバーライドは、祖先クラスで宣言されたメソッド/プロパティなどに新しい機能または追加機能を提供していることを示します。Shadowsは実際にコンパイラーをだまして、親メソッド/プロパティなどが存在しないと考えさせます。
私は影を使いません。オーバーライドに固執します。VBが何年にもわたって提供してきたこれらのタイプの便利な小さな「機能」は、いつかあなたに悲しみをもたらすことになります。