Au travail, je travaille fréquemment sur des projets où de nombreuses propriétés de certains objets doivent être définies lors de leur construction ou au début de leur vie. Pour des raisons 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
Semble beaucoup mieux que
Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines
pour les instructions très longues qui définissent simplement les propriétés.
J'ai remarqué qu'il y a des problèmes avec l'utilisation de With
lors du débogage; cependant, je me demandais s'il y avait des raisons impérieuses d'éviter d'utiliser With
dans la pratique? J'ai toujours supposé que le code généré via 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 ressens comme étant plus lisible.
Si vous avez des noms de variables 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, il y a même des avantages en termes de performances par rapport au premier exemple parce que dans le premier exemple, je récupère l'utilisateur chaque fois que j'accède à une propriété utilisateur et dans le cas AVEC, 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 j'opterais plutôt pour l'instruction WITH, elle a l'air plus propre.
Et je viens de prendre cela comme exemple, alors ne vous plaignez pas d'une classe avec de nombreux mots clés, un autre exemple pourrait être comme: WITH RefundDialog.RefundDatagridView.SelectedRows (0)
Dans la pratique, il n'y a pas vraiment d'arguments contraignants. Je ne suis pas un fan, mais c'est une préférence personnelle, il n'y a pas de données empiriques pour suggérer que la construction With
est mauvaise.
Dans .NET, il compile exactement le même code que la qualification complète du nom d'objet, il n'y a donc pas de pénalité de performance pour ce sucre. J'ai vérifié cela en compilant, puis en désassemblant, 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émontage est le suivant - notez que les appels à la méthode Append
de sb2
Sont identiques aux appels de l'instruction With
pour 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, allez-y; il n'y a aucune raison impérieuse de ne pas le faire.
(Au fait, Tom , je voudrais savoir ce qui s'est passé avec le débogueur - je ne me souviens pas avoir vu de comportement inhabituel dans le débogueur basé sur une instruction With
, donc je suis curieux de savoir quel comportement vous avez vu.)
Il y a une différence entre utiliser With et faire des références répétitives à un objet, ce qui est subtil mais devrait être gardé à 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 suivantes 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érer:
Dim AA As AAClass = GetNextAAObject()
With AA
AA = GetNextAAObject()
'// Setting property of original AA instance, not later instance
.SomeProperty = SomeValue
End With
Ainsi, l'instruction WITH n'est pas simplement du 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 question de lisibilité. Comme tout sucre syntaxique, il peut être surutilisé.
Embrace it IF vous définissez plusieurs membres d'un objet sur quelques lignes
With myObject
.Property1 = arg1
.Property2 = arg2
...
éviter 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 très difficile de se souvenir de ce qui a été déclaré en haut du bloc. Pour des raisons évidentes, je ne fournirai pas d'exemple de code aussi salissant
Là où il rend le code véritablement plus lisible, allez-y. Où cela le rend moins lisible, évitez-le - en particulier, je vous suggère d'éviter d'imbriquer les instructions With.
C # 3.0 a cette fonctionnalité uniquement pour l'initialisation d'objet:
var x = new Whatever { PropertyA=true, PropertyB="Inactive" };
Non seulement cela est à peu près nécessaire pour LINQ, mais cela a également un sens en termes de où la syntaxe n'indique pas une odeur de code. Je trouve généralement que lorsque j'exécute 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 sur l'objet lui-même.
Une remarque sur votre exemple - avez-vous vraiment besoin du "Moi"? Pourquoi ne pas simplement écrire:
PropertyA = True
PropertyB = "Inactive"
? "Moi" est certainement 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 ou propriétés d'instance, je pense que cela peut indiquer que vos classes sont trop grandes ( Large Class smell ). 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 violez probablement Loi Demeter
Je n'utilise pas VB.NET (j'avais l'habitude d'utiliser du VB simple) mais ...
Le premier point est-il obligatoire? Si c'est le cas, je ne vois aucun problème. En Javascript, le résultat de l'utilisation de with
est qu'une propriété d'un objet ressemble exactement à une variable ordinaire, et que est très dangereux, car vous ne voyez pas si vous 'accède à une propriété ou une variable, et donc, with
est quelque chose à éviter.
Non seulement son utilisation est plus facile pour les yeux, mais pour un accès répété aux propriétés d'un objet, il est susceptible d'être plus rapide, car l'objet n'est récupéré dans la chaîne de méthodes qu'une seule fois, et pas une fois pour chaque propriété.
Je suis d'accord avec d'autres réponses que vous devez éviter l'utilisation imbriquée de with
, pour la même raison que pour éviter with
tout à fait en Javascript: parce que vous ne voyez plus à quel objet votre propriété appartient .
Le "avec" est fondamentalement la "cascade" de Smalltalk. C'est un modèle du livre Smalltalk Best Practice Patterns de Kent Beck.
Un résumé du modèle: utilisez-le quand il est logique de regrouper les messages envoyés à l'objet. Ne l'utilisez pas s'il se trouve que certains messages sont envoyés au même objet.
ÉVITEZ le AVEC bloc à tout prix (même lisibilité). Deux raisons:
Pour décrire cela, nous avons un exemple (cassé) d'un manuel que mon collègue a dû demander à l'auteur (c'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)
.Charge()
Terminer par
Les commandes OrderBy et ThenBy n'ont aucun effet . SI vous reformatez le code en supprimant UNIQUEMENT le With et End With, et en ajoutant des caractères de continuation de ligne à la fin des trois premières lignes ... cela fonctionne (comme indiqué 15 pages plus tard de la même manière cahier de texte).
Nous n'avons plus besoin de raison pour chercher et détruire AVEC des blocs. Ils n'avaient de sens que dans un cadre interprété .
Il y a un problème lorsque vous l'utilisez avec des structures, c'est-à-dire que vous ne pouvez pas définir leurs champs, car vous travaillez sur une copie locale (faite au moment de l'entrée dans avec le bloc) de l'expression "avec" et ne travaillez pas avec une (copie d'une) référence d'objet dans ce cas:
Le type de données 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 produit autre chose qu'un objet, vous pouvez uniquement lire les valeurs de ses membres ou appeler des méthodes, et vous obtenez une erreur si vous essayez d'affecter des valeurs aux membres d'une structure utilisée dans une instruction With ... End With. Il s'agit de la même erreur que vous obtiendriez si vous appeliez une méthode qui renvoyait une structure et accédait immédiatement et attribuait une valeur à un membre du résultat de la fonction, comme GetAPoint (). X = 1. Le problème dans les deux cas est que le la structure n'existe que sur la pile des appels et il n'y a aucun moyen qu'un membre de structure modifié dans ces situations puisse écrire dans un emplacement tel que tout autre code du programme puisse observer le changement.
L'objetExpression est évalué une fois, lors de l'entrée dans le bloc. Vous ne pouvez pas réaffecter objectExpression à partir du bloc With.
suppose que le compilateur aurait pu être un peu plus intelligent si vous passiez à l'instruction un nom de structure au lieu d'une expression qui renvoie une structure, mais il semble que ce ne soit pas