Pregunta

En el trabajo, con frecuencia estoy trabajando en proyectos donde se deben establecer numerosas propiedades de ciertos objetos durante su construcción o temprano durante su vida útil. En aras de la conveniencia y la legibilidad, a menudo uso la instrucción With para establecer estas propiedades. Me parece que

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

Se ve mucho mejor que

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

para declaraciones muy largas que simplemente establecen propiedades.

He notado que hay algunos problemas con el uso de With durante la depuración; sin embargo, me preguntaba si había alguna razón convincente para evitar usar With en la práctica . Siempre he asumido que el código generado a través del compilador para los dos casos anteriores es básicamente el mismo, por eso siempre he elegido escribir lo que siento que es más legible.

¿Fue útil?

Solución

Si tiene nombres variables largos y terminaría con:

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

entonces usaría WITH para hacerlo más legible:

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

En el ejemplo posterior, incluso hay beneficios de rendimiento sobre el primer ejemplo porque en el primer ejemplo estoy buscando al usuario cada vez que accedo a una propiedad de usuario y en el caso WITH solo obtengo al usuario una vez.

Puedo obtener el aumento de rendimiento sin usar con, así:

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"

Pero preferiría la instrucción WITH, parece más limpia.

Y acabo de tomar esto como un ejemplo, así que no se queje de una clase con muchas palabras clave, otro ejemplo podría ser: WITH RefundDialog.RefundDatagridView.SelectedRows (0)

Otros consejos

En la práctica, no hay puntos realmente convincentes en su contra. No soy un fanático, pero es una preferencia personal, no hay datos empíricos que sugieran que la construcción With es mala.

En .NET, compila exactamente el mismo código que la calificación completa del nombre del objeto, por lo que no hay penalización de rendimiento para este azúcar. Verifiqué esto compilando y luego desarmando la siguiente clase 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

El desensamblaje es el siguiente: tenga en cuenta que las llamadas al método sb2 Append se ven idénticas a las instrucciones de la instrucción With para < 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

Entonces, si te gusta y lo encuentras más legible, hazlo; No hay una razón convincente para no hacerlo.

(Por cierto, Tom , estoy interesado en saber qué sucedió con el depurador: puedo ' No recuerdo haber visto ningún comportamiento inusual en el depurador basado en una instrucción With , así que tengo curiosidad por saber qué comportamiento viste).

Hay una diferencia entre usar With y hacer referencias repetidas a un objeto, lo cual es sutil pero debería tenerse en cuenta, creo.

Cuando se usa una instrucción WITH, crea una nueva variable local que hace referencia al objeto. Las referencias posteriores que usan .xx son referencias a propiedades de esa referencia local. Si durante la ejecución de la instrucción WITH, se cambia la referencia de variable original, el objeto al que hace referencia WITH no cambia. Considere:

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

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

Entonces, la declaración WITH no es simplemente azúcar sintáctica, es realmente una construcción diferente. Si bien es poco probable que codifique algo explícito como el anterior, en algunas situaciones esto puede ocurrir inadvertidamente, por lo que debe tener en cuenta el problema. La situación más probable es cuando puede atravesar una estructura como una red de objetos cuyas interconexiones pueden cambiarse implícitamente mediante el establecimiento de propiedades.

Se trata de legibilidad. Como todo azúcar sintáctico, puede ser usado en exceso .

Abrácelo SI está configurando varios miembros de un objeto en unas pocas líneas

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

Evite hacer cualquier otra cosa con " Con "

Si escribe un bloque With que abarca entre 50 y 100 líneas e involucra muchas otras variables, puede hacer que REALMENTE sea difícil recordar lo que se declaró en la parte superior del bloque. Por razones obvias, no proporcionaré un ejemplo de un código tan desordenado

Donde haga que el código sea más legible, hágalo. Cuando lo haga menos legible, evítelo, en particular, le sugiero que evite anidar con declaraciones.

C # 3.0 tiene esta característica únicamente para la inicialización de objetos:

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

Esto no solo es bastante necesario para LINQ, sino que también tiene sentido en términos de dónde la sintaxis no indica un olor a código. Por lo general, encuentro que cuando realizo muchas operaciones diferentes en un objeto más allá de su construcción inicial, esas operaciones deben encapsularse como una sola en el objeto mismo.

Una nota sobre su ejemplo: ¿realmente necesita el " Yo " ¿en absoluto? ¿Por qué no simplemente escribir:

PropertyA = True
PropertyB = "Inactive"

? Seguramente '' Yo '' está implícito en ese caso ...

Sospecharía del código que usa mucho esta palabra clave: si se usa para facilitar la configuración de muchas variables de instancia o propiedades, creo que esto puede indicar que sus clases son demasiado grandes ( Olor de clase grande ). Si lo usa para reemplazar largas cadenas de llamadas como esta:

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"

entonces probablemente esté violando la Ley de Demeter

No uso VB.NET (solía usar VB simple) pero ...

¿Es obligatorio el punto inicial? Si es así, entonces no veo un problema. En Javascript, el resultado de usar con es que una propiedad de un objeto se ve igual que una variable simple, y eso es muy peligroso, como no ve si está accediendo a una propiedad o una variable, y por lo tanto, con es algo que debe evitarse.

No solo su uso es más fácil para los ojos, sino que para el acceso repetido a las propiedades de un objeto, es probable que sea más rápido, ya que el objeto se obtiene a través de la cadena del método solo una vez, y no una vez por cada propiedad. p>

Estoy de acuerdo con otras respuestas de que debe evitar el uso anidado de con , por la misma razón que para evitar with por completo en Javascript: porque ya no ver a qué objeto pertenece su propiedad.

El 'con' es básicamente la 'cascada' de Smalltalk. Es un patrón en el libro de patrones de mejores prácticas Smalltalk de Kent Beck.

Un resumen del patrón: utilícelo cuando tenga sentido agrupar los mensajes enviados al objeto. No lo use si solo se trata de algunos mensajes enviados al mismo objeto.

EVITE el WITH Block a toda costa (incluso legibilidad). Dos razones:

  1. la La documentación de Microsoft sobre Con ... Finalizar con dice que en algunas circunstancias, crea una copia de los datos en la pila, por lo que cualquier cambio que realice será descartado.
  2. Si lo usa para consultas LINQ, los resultados lambda NO encadenan y, por lo tanto, el resultado de cada cláusula intermedia se descarta.

Para describir esto, tenemos un ejemplo (roto) de un libro de texto sobre el cual mi compañero de trabajo tuvo que preguntarle al autor (de hecho es incorrecto, los nombres se han cambiado para proteger ... lo que sea):

  

Con dbcontext.Blahs
    .OrderBy (Function (currentBlah) currentBlah.LastName)
    .ThenBy (Function (currentBlah) currentBlah.FirstName)
    .Load ()
  Terminar con

OrderBy y ThenBy no tienen ningún efecto . SI reformatea el código SOLO soltando el y con Fin y agregando caracteres de continuación de línea al final de las primeras tres líneas ... funciona (como se muestra 15 páginas más tarde en el mismo libro de texto) .

No necesitamos más razones para buscar y destruir WITH Blocks. Solo tenían significado en un marco Interpretado .

Hay un problema al usarlo con estructuras, es decir, no puede establecer sus campos, ya que está trabajando en una copia local (hecha en el momento de la entrada con el bloque) del " con " expresión y no funciona con una (copia de) referencia de objeto en ese caso:

  

El tipo de datos de objectExpression puede ser cualquier clase o tipo de estructura   o incluso un tipo elemental de Visual Basic como Integer. Si   objectExpression da como resultado cualquier cosa que no sea un objeto, puede   solo lea los valores de sus miembros o métodos de invocación, y obtendrá un   error si intenta asignar valores a miembros de una estructura utilizada en un   Con ... Finalizar con la declaración. Este es el mismo error que obtendrías si   invocó un método que devolvió una estructura y accedió de inmediato   y asignó un valor a un miembro del resultado de la función, como   GetAPoint (). X = 1. El problema en ambos casos es que la estructura   existe solo en la pila de llamadas, y no hay forma de que se modifique   miembro de la estructura en estas situaciones puede escribir en una ubicación tal que   cualquier otro código en el programa puede observar el cambio.

     

La objectExpression se evalúa una vez, al ingresar al bloque. usted   no se puede reasignar la objectExpression desde dentro del bloque With.

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

supongo que el compilador podría haber sido un poco más inteligente si pasa a la instrucción un nombre de estructura en lugar de una expresión que devuelve una estructura, pero parece que no lo es

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top