Domanda

Al lavoro, sto spesso lavorando a progetti in cui numerose proprietà di determinati oggetti devono essere impostate durante la loro costruzione o all'inizio della loro vita. Per motivi di praticità e leggibilità, spesso utilizzo l'istruzione With per impostare queste proprietà. Lo trovo

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

Sembra molto meglio di

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

per dichiarazioni molto lunghe che semplicemente impostano le proprietà.

Ho notato che ci sono alcuni problemi con l'utilizzo di With durante il debug; tuttavia, mi chiedevo se ci fossero motivi validi per evitare di usare With in pratica ? Ho sempre supposto che il codice generato tramite il compilatore per i due casi precedenti sia sostanzialmente lo stesso, motivo per cui ho sempre scelto di scrivere ciò che ritengo più leggibile.

È stato utile?

Soluzione

Se hai nomi di variabili lunghi e finiresti 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

quindi userei WITH per renderlo più leggibile:

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

Nell'esempio successivo ci sono anche vantaggi in termini di prestazioni rispetto al primo esempio perché nel primo esempio sto recuperando l'utente ogni volta che accedo a una proprietà dell'utente e nel caso WITH prendo l'utente solo una volta.

Posso ottenere il guadagno in termini di prestazioni senza utilizzare con, in questo modo:

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"

Ma preferirei invece l'affermazione WITH, sembra più pulita.

E l'ho appena preso come esempio, quindi non lamentarti di una lezione con molte parole chiave, un altro esempio potrebbe essere: WITH RefundDialog.RefundDatagridView.SelectedRows (0)

Altri suggerimenti

In pratica, non ci sono punti davvero convincenti contro di essa. Non sono un fan, ma è una preferenza personale, non ci sono dati empirici che suggeriscono che il costrutto With sia sbagliato.

In .NET, si compila esattamente con lo stesso codice della qualifica completa del nome dell'oggetto, quindi non ci sono penalità di prestazione per questo zucchero. L'ho accertato compilando, quindi disassemblando, la seguente classe 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

Lo smontaggio è il seguente: si noti che le chiamate al metodo sb2 del Aggiungi sembrano identiche all'istruzione With che richiede < 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

Quindi, se ti piace, e lo trovi più leggibile, provalo; non c'è motivo convincente di non farlo.

(A proposito, Tom , sono interessato a sapere cosa è successo con il debugger: posso " ricordo di aver mai visto un comportamento insolito nel debugger basato su un'istruzione With , quindi sono curioso di sapere quale comportamento hai visto.)

C'è una differenza tra l'uso di With e la creazione di riferimenti ripetuti a un oggetto, che è sottile ma dovrebbe essere tenuto a mente, penso.

Quando viene utilizzata un'istruzione WITH, viene creata una nuova variabile locale che fa riferimento all'oggetto. I riferimenti successivi che utilizzano .xx sono riferimenti alle proprietà di quel riferimento locale. Se durante l'esecuzione dell'istruzione WITH, il riferimento alla variabile originale viene modificato, l'oggetto a cui fa riferimento WITH non cambia. Prendere in considerazione:

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

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

Quindi, l'istruzione WITH non è semplicemente zucchero sintattico, è davvero un costrutto diverso. Sebbene sia improbabile che tu codifichi qualcosa di esplicito come sopra, in alcune situazioni ciò potrebbe accadere inavvertitamente, quindi dovresti essere consapevole del problema. La situazione più probabile è quella in cui potresti attraversare una struttura come una rete di oggetti le cui interconnessioni possono essere implicitamente modificate impostando le proprietà.

Si tratta di leggibilità. Come tutto lo zucchero sintattico, può essere abusato .

Abbraccialo SE stai impostando diversi membri di un oggetto su poche righe

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

Evita di fare qualsiasi altra cosa con " Con "

Se scrivi un blocco With che si estende per 50-100 righe e coinvolge molte altre variabili, può rendere MOLTO difficile ricordare ciò che è stato dichiarato all'inizio del blocco. Per ovvie ragioni, non fornirò un esempio di tale codice disordinato

Dove rende il codice davvero più leggibile, cercalo. Dove lo rende meno leggibile, evitalo - in particolare, ti suggerisco di evitare di annidare con le dichiarazioni.

C # 3.0 ha questa funzione esclusivamente per l'inizializzazione dell'oggetto:

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

Questo non è solo abbastanza richiesto per LINQ, ma ha anche senso in termini di dove la sintassi non indica un odore di codice. Di solito trovo che quando eseguo molte operazioni diverse su un oggetto oltre la sua costruzione iniziale, tali operazioni dovrebbero essere incapsulate come singole sull'oggetto stesso.

Una nota sul tuo esempio: hai davvero bisogno di " Me " affatto? Perché non scrivere semplicemente:

PropertyA = True
PropertyB = "Inactive"

? Sicuramente "io" è implicito in quel caso ...

Sarei sospettoso del codice che usa molto questa parola chiave: se viene utilizzato per semplificare l'impostazione di molte variabili o proprietà dell'istanza, penso che ciò possa indicare che le tue classi sono troppo grandi ( Odore di grande classe ). Se lo usi per sostituire lunghe catene di chiamate come questa:

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"

allora probabilmente stai violando Demeter Law

Non uso VB.NET (ero solito usare VB normale) ma ...

Il punto iniziale è obbligatorio? Se è così, allora non vedo un problema. In Javascript, il risultato dell'uso di con è che una proprietà di un oggetto ha lo stesso aspetto di una semplice variabile e che è molto pericoloso, come non vedi se stai accedendo a una proprietà o una variabile, e quindi con è qualcosa da evitare.

Non solo è più facile da usare per gli occhi, ma per un accesso ripetuto alle proprietà di un oggetto, è probabile che sia più veloce, poiché l'oggetto viene recuperato attraverso la catena del metodo solo una volta, e non una volta per ogni proprietà.

Sono d'accordo con altre risposte che dovresti evitare l'uso annidato di con , per lo stesso motivo per cui evitare con completamente in Javascript: perché non sei più vedere a quale oggetto appartiene la tua proprietà.

Il 'con' è fondamentalmente la 'cascata' da Smalltalk. È uno schema nel libro sulle buone pratiche Smalltalk di Kent Beck.

Un riepilogo del modello: usalo quando ha senso raggruppare i messaggi inviati all'oggetto. Non usarlo se capita che si tratti di alcuni messaggi inviati allo stesso oggetto.

EVITARE il WITH Block a tutti i costi (anche leggibilità). Due motivi:

  1. la Documentazione Microsoft su With ... End With afferma che in alcune circostanze, crea una copia dei dati nello stack, quindi tutte le modifiche apportate verranno eliminate.
  2. Se lo usi per le query LINQ, i risultati lambda NON si incatenano e quindi il risultato di ogni clausola intermedia viene eliminato.

Per descriverlo, abbiamo un esempio (rotto) di un libro di testo che il mio collega ha dovuto chiedere all'autore (è davvero errato, i nomi sono stati cambiati per proteggere ... qualunque cosa):

  

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

OrderBy e ThenBy non hanno nessun effetto . SE riformatta il codice rilasciando SOLO con Con e Termina con e aggiungendo caratteri di continuazione di riga alla fine delle prime tre righe ... funziona (come mostrato 15 pagine dopo nello stesso libro di testo) .

Non abbiamo bisogno di ulteriori motivi per cercare e distruggere WITH Blocks. Avevano significato solo in un quadro interpretato .

C'è un gotcha quando lo usi con le strutture, ovvero non puoi impostare i loro campi, poiché stai lavorando su una copia locale (fatta al momento dell'entrata con blocco) di " con " con " espressione e non funziona con un riferimento (copia di un) oggetto in quel caso:

  

Il tipo di dati di objectExpression può essere qualsiasi classe o tipo di struttura   o anche un tipo elementare di Visual Basic come Integer. Se   objectExpression risulta in qualcosa di diverso da un oggetto, è possibile   leggi solo i valori dei suoi membri o invoca metodi e otterrai un   errore se si tenta di assegnare valori ai membri di una struttura utilizzata in a   Con ... Termina con dichiarazione. Questo è lo stesso errore che otterresti se tu   invocò un metodo che restituiva una struttura e accedeva immediatamente   e assegnato un valore a un membro del risultato della funzione, ad esempio   GetAPoint (). X = 1. Il problema in entrambi i casi è che la struttura   esiste solo nello stack di chiamate e non è possibile modificare   il membro di struttura in queste situazioni può scrivere in una posizione tale   qualsiasi altro codice nel programma può osservare il cambiamento.

     

L'oggetto objectExpression viene valutato una volta, al momento dell'entrata nel blocco. tu   non è possibile riassegnare objectExpression all'interno del blocco With.

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

suppongo che il compilatore avrebbe potuto essere un po 'più intelligente se passassi con istruzione un nome di struttura invece di un'espressione che restituisce una struttura, ma sembra che non sia

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top