VB.NET与'声明 – 拥抱还是避免?

在工作中,我经常在一些项目中进行项目,在项目中必须在施工期间或者在生命早期设置特定物体的许多属性。 为了方便和易读,我经常使用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 

非常长的陈述,简单地设置属性。

我注意到在debugging时使用With有一些问题; 然而, 我想知道是否有任何令人信服的理由,以避免使用在实践中 ? 我总是认为通过编译器生成的代码基本上是相同的,这就是为什么我总是select写我觉得更可读。

如果你有很长的variables名,并且最终会:

 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情况下,我只需要一次提取用户。

没有使用,我可以获得性能增益,如下所示:

 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" 

但是我会selectWITH语句,看起来更干净。

我只是把这个作为一个例子,所以不要抱怨有很多关键字的类,另一个例子可能是这样的: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 

反汇编如下 – 请注意,对sb2Append方法的调用看起来与With语句调用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 

所以如果你喜欢它,并且发现它更具可读性,那就去做吧。 没有强制性的理由不这样做。

(顺便说一下,我有兴趣知道debugging器发生了什么事情 – 我根本不记得在debugging器中看到任何基于With语句的exception行为,所以我很想知道你做了什么样的行为看到。)

使用With和重复引用一个对象是有区别的,这个对象是微妙的,但是我应该牢记在心。

当使用WITH语句时,它会创build一个引用该对象的新本地variables。 后续使用.xx的引用是对该本地引用属性的引用。 如果在执行WITH语句的过程中,原来的variables引用被改变,那么WITH引用的对象不会改变。 考虑:

 Dim AA As AAClass = GetNextAAObject() With AA AA = GetNextAAObject() '// Setting property of original AA instance, not later instance .SomeProperty = SomeValue End With 

所以,WITH语句不是简单的语法糖,而是真正的不同的构造。 虽然你不可能像上面那样明确地编写代码,但在某些情况下,这可能会在无意中发生,所以你应该知道这个问题。 最有可能的情况是你可能会遍历一个结构,比如一个对象的networking,这些对象的互连我可以通过设置属性来隐式地改变。

这都是关于可读性的。 像所有的语法糖一样,它可以被过度使用

拥抱它,如果你在几行设置一个对象的几个成员

 With myObject .Property1 = arg1 .Property2 = arg2 ... 

避免做任何事情与“与”

如果你写了一个跨越50-100行的With块,并且涉及很多其他的variables,那么可以很难记住块顶部声明的内容。 出于显而易见的原因,我不会提供这样的乱码的例子

在代码真正更可读的地方,去做吧。 在那里它可读性较差 ,避免它 – 特别是,我build议你避免嵌套语句。

C#3.0具有此function仅用于对象初始化:

 var x = new Whatever { PropertyA=true, PropertyB="Inactive" }; 

这不仅仅是LINQ所需要的,而且在语法不代表代码异味的地方也是很有意义的。 我通常会发现,当我在一个超出其最初构造的对象上执行许多不同的操作时,这些操作应该被封装为一个单独的对象本身。

一个关于你的例子的说明 – 你真的需要“我”吗? 为什么不写:

 PropertyA = True PropertyB = "Inactive" 

? 在这种情况下,肯定“我”是隐含的

我会怀疑使用很多这个关键字的代码:如果它被用来更容易地设置大量的实例variables或属性我认为这可能表明你的类太大(大类气味 )。 如果你用它来代替这样长的呼叫链:

 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" 

那么你可能违反了德米特法

“with”基本上是Smalltalk的“级联”。 这是Kent Beck的Smalltalk最佳实践模式书中的一种模式。

模式的摘要:在将发送给对象的消息进行分组是有意义的时使用它。 如果碰巧发送到同一个对象的消息不要使用它。

我不使用VB.NET(我曾经使用普通的VB),但…

领先的点是强制性的吗? 如果是这样,那么我不会看到一个问题。 在Javascript中,使用的结果是一个对象的属性看起来与一个普通variables相同, 是非常危险的,因为你不知道你是否访问一个属性或variables, with是要避免的。

它不仅在眼睛上的使用更容易,而且对于重复访问对象的属性,它可能会更快,因为对象只通过方法链获取一次,而不是每个属性一次。

我同意其他答复,你应该避免嵌套使用,出于同样的原因,为什么要完全避免with Javascript:因为你不再看到你的财产属于什么对象。

不惜一切代价避免WITH块 (甚至可读性)。 两个原因:

  1. 有关With … End With的Microsoft文档指出,在某些情况下,它会在堆栈上创build数据副本,因此您所做的任何更改都将被丢弃。
  2. 如果您将它用于LINQ查询,那么lambda结果不会链,因此每个中间子句的结果都将被丢弃。

为了描述这一点,我们有一个来自教科书的(破碎的)例子,我的同事不得不问作者(这确实是不正确的,名称已经改变,以保护…)

用dbcontext.Blahs
.OrderBy(函数(currentBlah)currentBlah.LastName)
.ThenBy(函数(currentBlah)currentBlah.FirstName)
。加载()
结束

OrderBy和ThenBy根本没有效果 。 如果只通过删除With和End With来重新格式化代码,并且在前三行的末尾添加行连续字符,它就起作用了(如同样的教科书中15页所示)。

我们不需要更多的理由来search和销毁块。 它们只在解释框架中有意义。

与结构一起使用时有一个问题,即你不能设置它们的字段,因为你正在使用“with”expression式来处理本地副本(在进入块的时候创build),而不是使用(副本)在这种情况下的对象引用:

objectExpression的数据types可以是任何类或结构types,甚至是Visual Basic基本types(如Integer)。 如果objectExpression导致除对象以外的任何内容,则只能读取其成员的值或调用方法,而如果尝试将值赋给With … End With语句中使用的结构的成员,则会出现错误。 如果你调用了一个返回结构的方法,并且立即访问并赋值给函数结果的成员,比如GetAPoint(),那么你将得到同样的错误。x = 1。两种情况下的问题都是结构只存在于调用栈中,在这种情况下,修改后的结构成员无法写入某个位置,程序中的任何其他代码都无法观察到该变化。

一旦进入块,objectExpression被评估一次。 您不能从With块内重新分配objectExpression。

https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/with-end-with-statement

如果你传递一个结构名称而不是返回一个结构的expression式,那么编译器可能会更聪明一些,但看起来不是

我很确定这只是VB.net,但我可能是错的。 作为一个C#的人,我总是有点嫉妒这个语法糖。