VB.NETの「With」ステートメント-受け入れるか避けるか
-
08-07-2019 - |
質問
職場では、特定のオブジェクトの多数のプロパティを、構築中または初期に設定する必要があるプロジェクトに頻繁に取り組んでいます。利便性と読みやすさのために、これらのプロパティを設定するために With
ステートメントをよく使用します。見つけた
With Me.Elements
.PropertyA = True
.PropertyB = "Inactive"
' And so on for several more lines
End With
より良く見える
Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines
プロパティを設定するだけの非常に長いステートメントの場合。
デバッグ中に With
を使用すると問題が発生することに気付きました。ただし、 With
を実際に使用することを避ける説得力のある理由があるかどうか疑問に思っていました?上記の2つのケースでコンパイラーを介して生成されたコードは基本的に同じであると常に考えていたため、読みやすいと思うものを常に選択することにしました。
解決
長い変数名があり、次のようになる場合:
UserHandler.GetUser.First.User.FirstName="Stefan"
UserHandler.GetUser.First.User.LastName="Karlsson"
UserHandler.GetUser.First.User.Age="39"
UserHandler.GetUser.First.User.Sex="Male"
UserHandler.GetUser.First.User.Occupation="Programmer"
UserHandler.GetUser.First.User.UserID="0"
....and so on
その後、WITHを使用して読みやすくします:
With UserHandler.GetUser.First.User
.FirstName="Stefan"
.LastName="Karlsson"
.Age="39"
.Sex="Male"
.Occupation="Programmer"
.UserID="0"
end with
最初の例ではユーザープロパティにアクセスするたびにユーザーを取得し、WITHケースではユーザーを1回のみ取得するため、後者の例では最初の例よりもパフォーマンスが向上しています。
次のように、withを使用せずにパフォーマンスを向上させることができます。
dim myuser as user =UserHandler.GetUser.First.User
myuser.FirstName="Stefan"
myuser.LastName="Karlsson"
myuser.Age="39"
myuser.Sex="Male"
myuser.Occupation="Programmer"
myuser.UserID="0"
しかし、代わりにWITHステートメントを使用すると、よりきれいに見えます。
これを例として取り上げたので、多くのキーワードを持つクラスについて文句を言うことはできません。別の例は次のようになります。WITH RefundDialog.RefundDatagridView.SelectedRows(0)
他のヒント
実際には、それに対して本当に説得力のあるポイントはありません。私はファンではありませんが、それは個人的な好みです。 With
構造が悪いことを示唆する経験的なデータはありません。
.NETでは、オブジェクト名を完全修飾するのとまったく同じコードにコンパイルされるため、このシュガーに対するパフォーマンスの低下はありません。これを確認するには、次のVB .NET 2.0クラスをコンパイルしてから分解します。
Imports System.Text
Public Class Class1
Public Sub Foo()
Dim sb As New StringBuilder
With sb
.Append("foo")
.Append("bar")
.Append("zap")
End With
Dim sb2 As New StringBuilder
sb2.Append("foo")
sb2.Append("bar")
sb2.Append("zap")
End Sub
End Class
逆アセンブリは次のとおりです。 sb2
の Append
メソッドの呼び出しは、 With
ステートメントの呼び出しと< code> sb :
.method public instance void Foo() cil managed
{
// Code size 91 (0x5b)
.maxstack 2
.locals init ([0] class [mscorlib]System.Text.StringBuilder sb,
[1] class [mscorlib]System.Text.StringBuilder sb2,
[2] class [mscorlib]System.Text.StringBuilder VB$t_ref$L0)
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: stloc.2
IL_0009: ldloc.2
IL_000a: ldstr "foo"
IL_000f: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_0014: pop
IL_0015: ldloc.2
IL_0016: ldstr "bar"
IL_001b: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_0020: pop
IL_0021: ldloc.2
IL_0022: ldstr "zap"
IL_0027: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_002c: pop
IL_002d: ldnull
IL_002e: stloc.2
IL_002f: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0034: stloc.1
IL_0035: ldloc.1
IL_0036: ldstr "foo"
IL_003b: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_0040: pop
IL_0041: ldloc.1
IL_0042: ldstr "bar"
IL_0047: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_004c: pop
IL_004d: ldloc.1
IL_004e: ldstr "zap"
IL_0053: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
IL_0058: pop
IL_0059: nop
IL_005a: ret
} // end of method Class1::Foo
それで、あなたがそれを好きで、より読みやすいと思うならば、それを手に入れてください。しない理由はありません。
(ところで、 Tom 、私はデバッガで何が起こったのかを知りたい-私はできる」 With
ステートメントに基づいてデバッガーで異常な動作を見たことを覚えていないので、どのような動作が見られたか知りたいです。)
Withの使用と、オブジェクトへの繰り返し参照の作成には違いがあります。これは微妙ですが、留意する必要があります。
WITHステートメントを使用すると、オブジェクトを参照する新しいローカル変数が作成されます。 .xxを使用する後続の参照は、そのローカル参照のプロパティへの参照です。 WITHステートメントの実行中に元の変数参照が変更された場合、WITHによって参照されるオブジェクトは変更されません。考慮:
Dim AA As AAClass = GetNextAAObject()
With AA
AA = GetNextAAObject()
'// Setting property of original AA instance, not later instance
.SomeProperty = SomeValue
End With
したがって、WITHステートメントは単なる構文上の砂糖ではなく、まったく別の構造です。上記のような明示的なコードを記述することはほとんどありませんが、状況によってはこれが意図せずに発生する可能性があるため、問題に注意する必要があります。最も可能性の高い状況は、プロパティを設定することで相互接続が暗黙的に変更される可能性のあるオブジェクトのネットワークなどの構造を横断する場合です。
読みやすさのすべてです。すべての構文糖と同様に、使い古されている。
IFを採用する数行にわたってオブジェクトの複数のメンバーを設定している
With myObject
.Property1 = arg1
.Property2 = arg2
...
回避&quot; with&quot;を使用して他の操作を行う
50〜100行にまたがる他の多くの変数を含むWithブロックを記述すると、ブロックの先頭で宣言されたものを思い出すのが非常に難しくなります。明らかな理由から、このような厄介なコードの例は提供しません
コードを本当に読みやすくするのであれば、それを試してください。読みにくい 場合は、避けてください-特に、Withステートメントのネストを避けることをお勧めします。
C#3.0には、オブジェクトの初期化専用のこの機能があります。
var x = new Whatever { PropertyA=true, PropertyB="Inactive" };
これは、LINQに必要なだけでなく、構文がコードの匂いを示さないという点でも意味があります。通常、オブジェクトの初期構築を超えて多くの異なる操作を実行する場合、それらの操作はオブジェクト自体で単一の操作としてカプセル化する必要があることがわかります。
例についての1つのメモ-「Me」が本当に必要ですか?まったく?書いてみませんか:
PropertyA = True
PropertyB = "Inactive"
?確かに「私」その場合は暗示されます...
このキーワードを大量に使用するコードには疑いがあります:多数のインスタンス変数またはプロパティの設定を容易にするために使用される場合、これはクラスが大きすぎることを示していると思われます(大きなクラスの匂い)。次のように呼び出しの長いチェーンを置き換えるために使用する場合:
UserHandler.GetUser.First.User.FirstName="Stefan"
UserHandler.GetUser.First.User.LastName="Karlsson"
UserHandler.GetUser.First.User.Age="39"
UserHandler.GetUser.First.User.Sex="Male"
UserHandler.GetUser.First.User.Occupation="Programmer"
UserHandler.GetUser.First.User.UserID="0"
その後、おそらくデメテル法に違反している
VB.NETは使用しません(以前はプレーンVBを使用していました)が...
先頭のドットは必須ですか?もしそうなら、私は問題を見ません。 JavaScriptでは、 with
を使用した結果、オブジェクトのプロパティは単純な変数とまったく同じに見え、それはは非常に危険です。プロパティまたは変数にアクセスしているため、 with
は避けるべきものです。
目に使いやすいだけでなく、オブジェクトのプロパティに繰り返しアクセスする場合は、オブジェクトがすべてのプロパティに対して1回ではなくメソッドチェーンを通じてフェッチされるため、より高速になる可能性があります。
with
をJavascriptで完全に回避する理由と同じ理由で、 with
のネストされた使用を回避する必要がある他の応答に同意します。プロパティが属するオブジェクトを確認します。
「with」は基本的にSmalltalkの「カスケード」です。これは、Kent BeckのSmalltalk Best Practice Patternsブックのパターンです。
パターンの概要:オブジェクトに送信されたメッセージをグループ化することが理にかなっている場合に使用します。同じオブジェクトに送信されたメッセージである場合は、使用しないでください。
すべての費用でブロック付きを避けます(読みやすささえ)。 2つの理由:
- With ... End Withに関するマイクロソフトのドキュメントには状況によっては、スタック上にデータのコピーが作成されるため、行った変更はすべて破棄されます。
- LINQクエリに使用すると、ラムダの結果はチェーンしないため、各中間句の結果は破棄されます。
これを説明するために、同僚が著者に質問しなければならなかった教科書の(壊れた)例があります(実際には間違っています。保護するために名前が変更されました...何でも):
dbcontext.Blahs
.OrderBy(Function(currentBlah)currentBlah.LastName)
.ThenBy(Function(currentBlah)currentBlah.FirstName)
.Load()
終わり
OrderByとThenByには効果なしがあります。 WithとEnd Withのみをドロップし、最初の3行の最後に行継続文字を追加するだけでコードを再フォーマットすると...(同じ教科書の 15ページ後に示すように) 。
WITHブロックを検索および破棄するのにこれ以上の理由は必要ありません。それらは、解釈済みフレームワークでのみ意味がありました。
「with」のローカルコピー(with blockでのエントリ時に作成)で作業しているため、構造体で使用する際に注意点があります。別名フィールドを設定することはできません。その場合、式とオブジェクトの参照(のコピー)を使用しない:
objectExpressionのデータ型は、任意のクラスまたは構造型にすることができます または、整数などのVisual Basicの基本型です。もし objectExpressionの結果はオブジェクト以外のものになります。 そのメンバーの値を読み取るか、メソッドを呼び出すだけで、 で使用される構造体のメンバーに値を割り当てようとするとエラー With ... End Withステートメント。これは、次の場合に表示されるエラーと同じです 構造体を返すメソッドを呼び出し、すぐにアクセスした 関数の結果のメンバーに次のような値を割り当てました。 GetAPoint()。x =1。両方の場合の問題は、構造 呼び出しスタック上にのみ存在し、変更される方法はありません これらの状況の構造体メンバーは、次のような場所に書き込むことができます。 プログラム内の他のコードは変更を監視できます。
objectExpressionは、ブロックに入るときに1回評価されます。君は Withブロック内からobjectExpressionを再割り当てできません。
構造体を返す式ではなく構造体名をwithステートメントに渡すと、コンパイラはもう少し賢くなったかもしれませんが、そうではないようです