example - La declaración ''con'' de VB.NET: ¿aceptar o evitar?
#region en vb net (11)
En el trabajo, con frecuencia trabajo en proyectos en los que deben establecerse numerosas propiedades de ciertos objetos durante su construcción o temprano durante su vida. Por razones de conveniencia y legibilidad, a menudo uso la declaración With
para establecer estas propiedades. encontré eso
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.
Me he dado cuenta de 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 supuesto que el código generado a través del compilador para los dos casos anteriores es básicamente el mismo y por eso siempre he elegido escribir lo que considero más legible.
Cuando haga que el código sea realmente más legible, adelante. 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. Normalmente 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 tu ejemplo: ¿realmente necesitas el "Yo"? ¿Por qué no solo escribir?
PropertyA = True
PropertyB = "Inactive"
? Seguramente "Me" está implicado en ese caso ...
El ''con'' es básicamente la ''cascada'' de Smalltalk. Es un patrón en el libro Smalltalk Best Patterns Patterns de Kent Beck.
Un resumen del patrón: úselo cuando tenga sentido agrupar los mensajes enviados al objeto. No lo use si resultan ser algunos mensajes enviados al mismo objeto.
En la práctica, no hay puntos convincentes contra eso. No soy fanático, pero esa es una preferencia personal, no hay datos empíricos que sugieran que el constructo With
sea malo.
En .NET, compila exactamente el mismo código que la calificación completa del nombre del objeto, por lo que no hay una penalización de rendimiento para este azúcar. Lo comprobé 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 desmontaje es el siguiente: tenga en cuenta que las llamadas al método Append
sb2
son idénticas a las llamadas a la declaración With
para 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, ve por él; no hay una razón convincente para no hacerlo.
(Por cierto, , estoy interesado en saber qué pasó con el depurador; no recuerdo haber visto ningún comportamiento inusual en el depurador basado en una declaración With
, así que tengo curiosidad por saber qué comportamiento usaste ver.)
Estoy bastante seguro de que esto es solo VB.net, pero podría estar equivocado. Siendo un chico C #, siempre estuve un poco celoso de ese azúcar sintáctico.
Hay un error al usarlo con estructuras, también conocido como no se pueden establecer sus campos, ya que está trabajando en una copia local (hecha en el momento de la entrada en el bloque) de la expresión "con" y no está trabajando con una (copia de una) referencia de objeto en ese caso:
El tipo de datos de objectExpression puede ser de cualquier clase o tipo de estructura o incluso de un tipo elemental de Visual Basic como Integer. Si objectExpression resulta en algo que no sea un objeto, solo puede leer los valores de sus miembros o invocar métodos, y obtendrá un error si intenta asignar valores a los miembros de una estructura utilizada en una declaración With ... End With. Este es el mismo error que obtendría si invocara un método que devolviera una estructura y accediera inmediatamente y le asignara un valor a un miembro del resultado de la función, como GetAPoint (). X = 1. El problema en ambos casos es que el la estructura existe solo en la pila de llamadas, y no hay forma de que un miembro de la estructura modificada en estas situaciones pueda escribir en una ubicación de manera que cualquier otro código en el programa pueda observar el cambio.
ObjectExpression se evalúa una vez, al ingresar al bloque. No puede reasignar objectExpression desde el bloque With.
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 es
Hay una diferencia entre usar With y hacer referencias repetitivas a un objeto, lo cual es sutil, pero debería tenerse en cuenta, creo.
Cuando se usa una declaración WITH, crea una nueva variable local que hace referencia al objeto. Las referencias posteriores que usan .xx son referencias a las propiedades de esa referencia local. Si durante la ejecución de la declaración WITH, se cambia la referencia de la variable original, el objeto al que hace referencia el WITH no cambia. Considerar:
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 podría ocurrir inadvertidamente, por lo que debe tener en cuenta el problema. La situación más probable es donde puede estar atravesando una estructura, como una red de objetos cuyas interconexiones pueden modificarse implícitamente configurando propiedades.
No uso VB.NET (solía usar VB simple) pero ...
¿El punto inicial es obligatorio? Si es así, entonces no veo un problema. En Javascript, el resultado de usar with
es que una propiedad de un objeto se ve exactamente igual que una variable simple, y eso es muy peligroso, ya que no se ve si está accediendo a una propiedad o una variable, y por lo tanto, with
es algo para evitar.
No solo es más fácil su uso para los ojos, sino también para el acceso repetido a las propiedades de un objeto, es probable que sea más rápido, ya que el objeto se busca a través de la cadena de métodos solo una vez, y no una vez por cada propiedad.
Estoy de acuerdo con otras respuestas con las que deberías evitar el uso anidado de, por la misma razón que por qué evitarlas with
completo en JavaScript: porque ya no ves a qué objeto pertenece tu propiedad.
Se trata de la legibilidad. Como todo el azúcar sintáctico, puede ser usado en exceso .
Acéptalo si estás 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 50-100 líneas e involucra muchas otras variables, puede hacer REALMENTE difícil recordar lo que se declaró en la parte superior del bloque. Por razones obvias, no proporcionaré un ejemplo de código tan desordenado
Sería sospechoso de código que usa mucho esta palabra clave: si se usa para facilitar el establecimiento 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 cadenas largas 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és violando la Ley Demeter
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 último ejemplo, incluso hay un beneficio de rendimiento con respecto al primer ejemplo, porque en el primer ejemplo estoy obteniendo el usuario cada vez que accedo a una propiedad del usuario y, en el caso de WITH, solo alcanzo al usuario una vez.
Puedo obtener la ganancia de rendimiento sin usar, 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 declaración WITH en su lugar, parece más limpia.
Y tomé esto como un ejemplo, así que no te quejes por una clase con muchas palabras clave, otro ejemplo podría ser: WITH RefundDialog.RefundDatagridView.SelectedRows (0)
EVITE el bloqueo WITH a toda costa (incluso legibilidad). Dos razones:
- la Documentación de Microsoft sobre Con ... Fin Con dice que en algunas circunstancias, crea una copia de los datos en la pila, por lo que cualquier cambio que realice será descartado.
- 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 que mi compañero de trabajo tuvo que preguntarle al autor (de hecho es incorrecto, los nombres han sido cambiados para proteger ... lo que sea):
Con dbcontext.Blahs
.OrderBy (Function (currentBlah) currentBlah.LastName)
.ThenBy (Function (currentBlah) currentBlah.FirstName)
.Carga()
Terminar con
El OrderBy y ThenBy no tienen ningún efecto . SI vuelve a formatear el código SÓLO bajando el Con y el Fin con, y agregando caracteres de continuación de línea al final de las tres primeras líneas ... funciona (como se muestra 15 páginas más adelante 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 .