VB.NET“与” 声明——拥抱还是避免?

发布于 2024-07-09 06:34:56 字数 586 浏览 7 评论 0原文

在工作中,我经常从事一些项目,其中某些对象的许多属性必须在其构建过程中或在其生命周期的早期进行设置。 为了方便和可读性,我经常使用 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? 我一直认为通过编译器为上述两种情况生成的代码基本上是相同的,这就是为什么我总是选择编写我认为更具可读性的代码。

At work, I'm frequently working on projects where numerous properties of certain objects have to be set during their construction or early during their lifetime. For the sake of convenience and readability, I often use the With statement to set these properties. I find that

With Me.Elements
    .PropertyA = True
    .PropertyB = "Inactive"
    ' And so on for several more lines
End With

Looks much better than

Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines

for very long statements that simply set properties.

I've noticed that there are some issues with using With while debugging; however, I was wondering if there were any compelling reasons to avoid using With in practice? I've always assumed the code generated via the compiler for the above two cases is basically the same which is why I've always chosen to write what I feel to be more readable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

英雄似剑 2024-07-16 06:34:56

如果你有很长的变量名并且最终会是:

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-情况下我只获取用户一次。

我可以在不使用 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)

If you have long variablenames and would end up with:

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

then I would use WITH to make it more readable:

With UserHandler.GetUser.First.User
    .FirstName="Stefan"
    .LastName="Karlsson"
    .Age="39"
    .Sex="Male"
    .Occupation="Programmer"
    .UserID="0"
end with

In the later example there are even performance benefit over the first example because in the first example Im fetching the user every time I access a user property and in the WITH-case I only fetch the user one time.

I can get the performance gain without using with, like this:

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"

But I would go for the WITH statement instead, it looks cleaner.

And I just took this as an example so dont complain over a class with many keywords, another example could be like: WITH RefundDialog.RefundDatagridView.SelectedRows(0)

贪了杯 2024-07-16 06:34:56

在实践中,并没有真正令人信服的理由反对它。 我不是粉丝,但这是个人偏好,没有经验数据表明 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

因此,如果您喜欢它,并且发现它更具可读性,那就去吧; 没有令人信服的理由不这样做。

(顺便说一句,Tom,我有兴趣知道调试器发生了什么——我不记得了基于 With 语句在调试器中看到任何异常行为,所以我很想知道您确实看到了什么行为。)

In practice, there are no really compelling points against it. I'm not a fan, but that's a personal preference, there's no empirical data to suggest that the With construct is bad.

In .NET, it compiles to exactly the same code as fully-qualifying the object name, so there is no performance penalty for this sugar. I ascertained this by compiling, then disassembling, the following VB .NET 2.0 class:

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

The disassembly is as follows -- note that the calls to sb2's Append method look identical to the With statement calls for 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

So if you like it, and find it more readable, go for it; there's no compelling reason not to.

(By the way, Tom, I'm interested in knowing what happened with the debugger -- I can't recall ever seeing any unusual behavior in the debugger based on a With statement, so I'm curious to know what behavior you did see.)

心病无药医 2024-07-16 06:34:56

使用 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 语句不仅仅是语法糖,它确实是一个不同的结构。 虽然您不太可能编写像上面这样的显式代码,但在某些情况下,这可能会无意中发生,因此您应该意识到这个问题。 最可能的情况是您可能正在遍历一个结构,例如对象网络,其互连可以通过设置属性隐式更改。

There is a difference between using With and making repeating references to an object, which is subtle but should be borne in mind, I think.

When a WITH statement is used, it creates a new local variable referencing the object. Subsequent references using .xx are references to properties of that local reference. If during the execution of the WITH statement, the original variable reference is changed, the object referenced by the WITH does not change. Consider:

Dim AA As AAClass = GetNextAAObject()
With AA
    AA = GetNextAAObject()

    '// Setting property of original AA instance, not later instance
    .SomeProperty = SomeValue
End With

So, the WITH statement is not simply syntactical sugar, it is genuinely a different construct. Whilst you would be unlikely to code something explicit like the above, in some situations this might occur inadvertently so you should be aware of the issue. The most likely situation is where you may be traversing a structure such as a network of objects whose interconnections my be implicitly changed by setting properties.

╭ゆ眷念 2024-07-16 06:34:56

这一切都与可读性有关。 像所有语法糖一样,它可能会被过度使用

拥抱它,如果您要在几行中设置对象的多个成员

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

避免使用“With”做任何其他事情

如果您编写一个跨越 50-100 行的 With 块并且涉及许多其他变量,这使得记住在块顶部声明的内容真的很困难。 出于显而易见的原因,我不会提供这种混乱代码的示例

It's all about readability. Like all syntactic sugar, it can be overused.

Embrace it IF you're setting several members of an object over a few lines

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

Avoid doing anything else with "With"

If you write a With block that spans 50-100 lines and involves lots of other variables it can make it REALLY difficult to remember what was declared at the top of the block. For obvious reasons, I won't provide an example of such messy code

别在捏我脸啦 2024-07-16 06:34:56

如果它确实使代码更具可读性,那就去做吧。 如果它导致可读性降低,请避免使用 - 特别是,我建议您避免嵌套 With 语句。

C# 3.0 具有专门用于对象初始化的此功能:

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

这不仅是 LINQ 所必需的,而且在语法不指示代码异味的情况下也是有意义的。 我通常发现,当我在一个对象上执行超出其初始构造的许多不同操作时,这些操作应该封装为对象本身的单个操作。

关于你的例子的一点说明——你真的需要“我”吗? 为什么不直接写:

PropertyA = True
PropertyB = "Inactive"

? 在这种情况下肯定暗示着“我”......

Where it makes the code genuinely more readable, go for it. Where it makes it less readable, avoid it - in particular, I suggest you avoid nesting With statements.

C# 3.0 has this feature solely for object initialization:

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

This is not only pretty much required for LINQ, but it also makes sense in terms of where the syntax doesn't indicate a code smell. I usually find that when I'm performing many different operations on an object beyond its initial construction, those operations should be encapsulated as a single one on the object itself.

One note about your example - do you really need the "Me" at all? Why not just write:

PropertyA = True
PropertyB = "Inactive"

? Surely "Me" is implied in that case...

℉服软 2024-07-16 06:34:56

我会对大量使用 this 关键字的代码表示怀疑:如果它用于更容易设置大量实例变量或属性,我认为这可能表明您的类太大( 大类气味)。 如果你用它来替换像这样的长链调用:

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"

那么你可能违反了 Demeter法律

I would be suspicious of code that uses a lot this keyword: if it is used to make easier to set lots of instance variables or properties I think this may indicate that your classes are too large ( Large Class smell ). If you use it to replace long chains of calls like this:

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"

then you are probably violating Demeter Law

爱要勇敢去追 2024-07-16 06:34:56

我不使用 VB.NET(我曾经使用纯 VB)但是...

前导点是强制性的吗? 如果是这样,那么我认为没有问题。 在 Javascript 中,使用 with 的结果是对象的属性看起来与普通变量一样,这是非常危险的,因为你看不到如果您要访问属性或变量,则应避免使用 with

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

我确实同意其他回复,即您应该避免嵌套使用 with,其原因与为什么在 Javascript 中完全避免 with 的原因相同:因为您不再看到什么对象你的财产属于。

I don't use VB.NET (I used to use plain VB) but...

Is the leading dot mandatory? If so, then I don't see a problem. In Javascript, the result of using with is that a property of an object looks just the same as a plain variable, and that is very dangerous, as you don't see if you're accessing a property or a variable, and thus, with is something to avoid.

Not only is its use easier on the eyes, but for repeated access to properties of an object, it's likely to be faster, as the object is fetched through the method chain only once, and not once for every property.

I do agree with other replies that you ought to avoid nested use of with, for the same reason as why to avoid with altogether in Javascript: because you no longer see what object your property belongs to.

樱花落人离去 2024-07-16 06:34:56

“with”基本上是来自 Smalltalk 的“cascade”。 它是 Kent Beck 的 Smalltalk 最佳实践模式书中的一个模式。

该模式的摘要:当对发送到对象的消息进行分组有意义时使用它。 如果恰好是发送到同一对象的一些消息,请不要使用它。

The 'with' is basically the 'cascade' from Smalltalk. It is a pattern in Kent Beck's Smalltalk Best Practice Patterns book.

A summary of the pattern: use it when it makes sense to group the messages sent to the object. Don't use it if it just happens to be some messages sent to the same object.

怪我入戏太深 2024-07-16 06:34:56

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

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

为了描述这一点,我们有一个来自教科书的(损坏的)示例,我的同事必须向作者询问(这确实不正确,名称已被更改以保护......无论如何):

使用 dbcontext.Blahs
.OrderBy(函数(currentBlah) currentBlah.LastName)
.ThenBy(函数(currentBlah) currentBlah.FirstName)
.Load()
结束于

OrderBy 和 ThenBy 根本没有效果。 如果您通过仅删除 With 和 End With 来重新格式化代码,并在前三行的末尾添加行继续字符......它可以工作(如同一教科书中15页后所示) 。

我们不需要更多的理由来搜索并销毁WITH 块。 它们仅在解释框架中才有意义。

AVOID the WITH Block at all costs (even readability). Two reasons:

  1. the Microsoft Documentation about With...End With says that in some circumstances, it creates a copy of the data on the stack, so any changes that you make will be thrown away.
  2. If you use it for LINQ Queries, the lambda results DO NOT Chain and so each intermediate clause's result is thrown away.

To describe this, we have a (broken) example from a Textbook that my co-worker had to ask the author about (it is indeed incorrect, the Names have been changed to protect... whatever):

With dbcontext.Blahs
.OrderBy(Function(currentBlah) currentBlah.LastName)
.ThenBy(Function(currentBlah) currentBlah.FirstName)
.Load()
End With

The OrderBy and ThenBy have No Effect at all. IF you reformat the code by ONLY dropping the With and End With, and adding line continuation characters at the end of the first three lines... it works (as shown 15 pages later in the same textbook).

We don't need any more reason to search and destroy WITH Blocks. They only had meaning in an Interpreted framework.

甜中书 2024-07-16 06:34:56

将其与结构一起使用时存在一个问题,即您无法设置它们的字段,因为您正在处理“with”表达式的本地副本(在进入 with 块时制作),而不是使用 (copy在这种情况下的 an) 对象引用:

objectExpression的数据类型可以是任何类或结构类型
甚至是 Visual Basic 基本类型,例如 Integer。 如果
objectExpression 会产生除对象之外的任何内容,您可以
仅读取其成员的值或调用方法,然后您会得到一个
如果您尝试将值分配给结构体中使用的成员,则会出现错误
With...End With 语句。 如果您这样做,您会得到同样的错误
调用返回结构并立即访问的方法
并将一个值赋给函数结果的成员,例如
GetAPoint().x = 1。这两种情况的问题是结构
只存在于调用堆栈中,并且无法修改
在这些情况下,结构成员可以写入这样的位置:
程序中的任何其他代码都可以观察到更改。

objectExpression 在进入块时计算一次。 你
无法从 With 块内重新分配 objectExpression。

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

猜测如果你传递给 with 语句 a ,编译器可能会更聪明一点结构名称而不是返回结构的表达式,但似乎不是

There's a gotcha when using it with structures, aka you can't set their fields, since you're working on a local copy (made at time of entry in with block) of the "with" expression and not working with a (copy of an) object reference in that case:

The data type of objectExpression can be any class or structure type
or even a Visual Basic elementary type such as Integer. If
objectExpression results in anything other than an object, you can
only read the values of its members or invoke methods, and you get an
error if you try to assign values to members of a structure used in a
With...End With statement. This is the same error you would get if you
invoked a method that returned a structure and immediately accessed
and assigned a value to a member of the function’s result, such as
GetAPoint().x = 1. The problem in both cases is that the structure
exists only on the call stack, and there is no way a modified
structure member in these situations can write to a location such that
any other code in the program can observe the change.

The objectExpression is evaluated once, upon entry into the block. You
can't reassign the objectExpression from within the With block.

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

guess the compiler could have been a bit more clever if you pass to with statement a structure name instead of an expression that returns a structure, but seems it's not

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文