Question

Au travail, je travaille fréquemment sur des projets dans lesquels de nombreuses propriétés de certains objets doivent être définies au cours de leur construction ou au début de leur vie. Par souci de commodité et de lisibilité, j’utilise souvent l’instruction With pour définir ces propriétés. Je trouve que

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

Parait bien mieux que

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

pour les très longues instructions qui définissent simplement les propriétés.

J'ai remarqué des problèmes d'utilisation de With lors du débogage; Cependant, je me demandais s'il existait des raisons impérieuses d'éviter d'utiliser avec dans la pratique ? J'ai toujours supposé que le code généré par le compilateur pour les deux cas ci-dessus était fondamentalement le même, c'est pourquoi j'ai toujours choisi d'écrire ce que je sens être plus lisible.

Était-ce utile?

La solution

Si vous avez des noms de variable longs et que vous vous retrouvez avec:

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

alors j'utiliserais WITH pour le rendre plus lisible:

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

Dans le dernier exemple, le premier exemple présente des avantages en termes de performances, car dans le premier exemple, je récupère l'utilisateur à chaque fois que j'accède à une propriété de l'utilisateur et, dans le cas WITH, je ne récupère l'utilisateur qu'une seule fois.

Je peux obtenir le gain de performances sans utiliser avec, comme ceci:

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"

Mais je choisirais plutôt l'instruction WITH, elle a l'air plus propre.

Et je viens de prendre cela comme exemple, alors ne vous plaignez pas pour une classe avec de nombreux mots-clés, un autre exemple pourrait être: WITH RefundDialog.RefundDatagridView.SelectedRows (0)

Autres conseils

En pratique, il n’existe aucun argument vraiment convaincant. Je ne suis pas un fan, mais c'est une préférence personnelle. Aucune donnée empirique ne permet de penser que la construction With est incorrecte.

Dans .NET, il compile exactement le même code que celui qui qualifie complètement le nom de l'objet. Il n'y a donc aucune pénalité de performance pour ce sucre. Je l'ai constaté en compilant, puis en démontant, la classe VB .NET 2.0 suivante:

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

Le désassemblage est le suivant - notez que les appels à la méthode Append de sb2 sont identiques à ceux de l'instruction With pour < 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

Donc si vous l’aimez et le trouvez plus lisible, foncez; il n'y a aucune raison impérieuse de ne pas le faire.

(Au fait, Tom , j'aimerais savoir ce qui s'est passé avec le débogueur - je peux Ne vous souvenez pas avoir jamais vu de comportement inhabituel dans le débogueur basé sur une instruction With , je suis donc curieux de savoir quel comportement vous avez vu.)

Il y a une différence entre utiliser avec et faire des références répétées à un objet, ce qui est subtil mais qu'il faut garder à l'esprit, je pense.

Lorsqu'une instruction WITH est utilisée, elle crée une nouvelle variable locale référençant l'objet. Les références ultérieures utilisant .xx sont des références aux propriétés de cette référence locale. Si, lors de l'exécution de l'instruction WITH, la référence de variable d'origine est modifiée, l'objet référencé par WITH ne change pas. Considérez:

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

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

L’instruction WITH n’est donc pas simplement un sucre syntaxique, c’est vraiment une construction différente. Bien qu'il soit peu probable que vous codiez quelque chose d'explicite comme ci-dessus, dans certaines situations, cela peut se produire par inadvertance. Vous devez donc être conscient du problème. La situation la plus probable est celle où vous pouvez traverser une structure telle qu'un réseau d'objets dont les interconnexions peuvent être implicitement modifiées en définissant des propriétés.

Tout est une question de lisibilité. Comme tout sucre syntaxique, il peut être trop utilisé .

Embrassez-le si vous définissez plusieurs membres d'un objet sur quelques lignes

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

Évitez de faire autre chose avec "Avec"

.

Si vous écrivez un bloc With qui s'étend sur 50 à 100 lignes et implique de nombreuses autres variables, il peut être VRAIMENT difficile de se souvenir de ce qui a été déclaré en haut du bloc. Pour des raisons évidentes, je ne donnerai pas d'exemple d'un code aussi compliqué

Là où cela rend le code vraiment plus lisible, allez-y. Si cela rend moins lisible, évitez-le - en particulier, je vous suggère d'éviter d'imbriquer des instructions With.

C # 3.0 possède cette fonctionnalité uniquement pour l'initialisation d'objet:

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

Cela est non seulement nécessaire pour LINQ, mais cela a également un sens en ce qui concerne les cas où la syntaxe n'indique pas une odeur de code. Je trouve généralement que lorsque j'effectue de nombreuses opérations différentes sur un objet au-delà de sa construction initiale, ces opérations doivent être encapsulées comme une seule opération sur l'objet lui-même.

Une remarque à propos de votre exemple: avez-vous vraiment besoin du " moi " du tout? Pourquoi ne pas simplement écrire:

PropertyA = True
PropertyB = "Inactive"

? Sûrement " moi " est impliqué dans ce cas ...

Je me méfierais du code qui utilise beaucoup ce mot clé: s'il est utilisé pour faciliter la définition de nombreuses variables d'instance ou propriétés, cela peut indiquer que vos classes sont trop volumineuses ( Odeur de grande classe ). Si vous l’utilisez pour remplacer de longues chaînes d’appels comme ceci:

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"

alors vous enfreignez probablement la loi Demeter

Je n'utilise pas VB.NET (j'avais l'habitude d'utiliser VB ordinaire) mais ...

Le premier point est-il obligatoire? Si oui, alors je ne vois pas de problème. En Javascript, le résultat de l'utilisation de avec est qu'une propriété d'un objet a exactement la même apparence qu'une variable simple, et que est très dangereux, comme vous ne le voyez pas. si vous accédez à une propriété ou à une variable et que, par conséquent, avec est quelque chose à éviter.

Non seulement son utilisation est plus simple sur le plan visuel, mais un accès répété aux propriétés d'un objet est probablement plus rapide car l'objet est extrait de la chaîne de méthodes une seule fois, et pas une fois pour toutes.

Je suis d'accord avec les autres réponses selon lesquelles vous devez éviter l'utilisation imbriquée de avec , pour les mêmes raisons que celles qui justifient d'éviter avec en Javascript: parce que vous ne voir à quel objet appartient votre propriété.

Le "avec" est essentiellement la "cascade" de Smalltalk. C’est un modèle du livre de pratiques optimales Smalltalk de Kent Beck.

Résumé du modèle: utilisez-le lorsqu'il est judicieux de regrouper les messages envoyés à l'objet. Ne l'utilisez pas s'il se trouve que certains messages ont été envoyés au même objet.

ÉVITEZ le WITH Block à tout prix (même sa lisibilité). Deux raisons:

  1. la La documentation de Microsoft sur la fin de la recherche avec indique que Dans certaines circonstances, il crée une copie des données sur la pile, ainsi toutes les modifications que vous apportez seront rejetées.
  2. Si vous l'utilisez pour les requêtes LINQ, les résultats lambda NE CHAÎNENT PAS et le résultat de chaque clause intermédiaire est alors jeté.

Pour décrire cela, nous avons un exemple (cassé) tiré d'un manuel que mon collègue devait interroger sur l'auteur (il est en effet incorrect, les noms ont été modifiés pour protéger ... peu importe):

  

Avec dbcontext.Blahs
    .OrderBy (Fonction (currentBlah) currentBlah.LastName)
    .ThenBy (Fonction (currentBlah) currentBlah.FirstName)
    .Load ()
  Terminer avec

OrderBy et ThenBy ont aucun effet . SI vous reformatez le code en supprimant UNIQUEMENT les caractères With et End With et en ajoutant des caractères de continuation de ligne à la fin des trois premières lignes ... cela fonctionne (comme le montre 15 pages plus tard dans le même manuel) .

Nous n'avons plus besoin de raison pour rechercher et détruire WITH Blocks. Ils n'avaient de sens que dans un cadre interprété .

Il est difficile de l'utiliser avec des structures, vous ne pouvez pas définir leurs champs, car vous travaillez sur une copie locale (créée au moment de l'entrée dans un bloc) de l'option "with". expression et ne travaillant pas avec une (copie d'une) référence d'objet dans ce cas:

  

Le type de données de objectExpression peut être n’importe quel type de classe ou de structure.   ou même un type élémentaire Visual Basic tel que Integer. Si   objectExpression a pour résultat autre chose qu'un objet, vous pouvez   seulement lire les valeurs de ses membres ou invoquer des méthodes, et vous obtenez un   erreur si vous essayez d'attribuer des valeurs aux membres d'une structure utilisée dans un   Avec ... Fin avec déclaration. C’est la même erreur que vous obtiendriez si vous   invoqué une méthode qui a retourné une structure et immédiatement accédé   et attribué une valeur à un membre du résultat de la fonction, tel que   GetAPoint (). X = 1. Le problème dans les deux cas est que la structure   n'existe que sur la pile d'appels, et il n'y a aucun moyen de modifier   membre de la structure dans ces situations peut écrire à un endroit tel que   tout autre code du programme peut observer le changement.

     

objectExpression est évalué une fois, lors de l'entrée dans le bloc. Vous   ne peut pas réaffecter objectExpression à partir du bloc With.

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

Je suppose que le compilateur aurait pu être un peu plus intelligent si vous passiez à instruction un nom de structure au lieu d'une expression qui renvoie une structure, mais il semble que ce ne soit pas

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top