
Selon
la note TIOBE en 2018, VB.NET a dépassé C # en popularité. Coïncidence ou non, mais en février, Eric Lippert, l'un des créateurs de C #, a
exhorté les lecteurs à prêter attention au blog de son ami, un ancien coéquipier du compilateur Roslyn et, en combinaison, un ardent fan de VB.NET,
Anthony Green . «Ces ressources sont des détails détaillés d'experts qui ne sont pas si faciles à trouver en lisant la documentation», écrit Eric. Nous présentons à votre attention la première partie de la traduction de l'article d'Anthony Green «Une liste exhaustive des différences entre VB.NET et C #». C'est peut-être précisément dans ces différences que réside le secret de la dynamique de la notation de ces langues.
Pendant près de la moitié de ma vie, j'ai assisté et participé à d'innombrables discussions sur la similitude ou la différence des deux langages .NET les plus populaires. D'abord, en tant qu'amateur, puis en tant que professionnel, et enfin, en tant que défenseur des clients, gestionnaire de programme et concepteur de langage, je peux dire sans exagérer combien de fois j'ai entendu ou lu quelque chose comme:
"... VB.NET n'est vraiment qu'une fine couche au-dessus d'IL, comme C # ..."
ou
"... VB.NET est en fait juste C # sans point-virgule ..."
Comme si les langues étaient une conversion XML ou une feuille de style.
Et si un visiteur ardent n'écrit pas cela dans un commentaire, alors souvent cela est impliqué dans des questions comme:
«Bonjour, Anthony! Je suis tombé sur une si petite différence en un seul endroit - est-ce un bug? Comment ces deux langues par ailleurs identiques, qui devraient être identiques au nom de tous les bons et les saints de ce monde, pourraient-elles se disperser en ce même endroit? Pourquoi avons-nous besoin d'une telle injustice?!«
Séparés », comme s'ils étaient les mêmes jusqu'à ce qu'une mutation se produise,
puis deviennent des espèces distinctes. Hah!
Mais je comprends ça. Avant de rejoindre Microsoft, j'aurais peut-être vaguement conservé cette idée et l'aurais utilisée comme argument pour répondre à des opposants ou pour rassurer quelqu'un. Je comprends son charme. Il est facile à comprendre et très facile à répéter. Mais en travaillant sur Roslyn (essentiellement en réécrivant VB et C # à partir de zéro) pendant 5 ans, j'ai réalisé à quel point
cette idée était sans ambiguïté . J'ai travaillé avec une équipe de développeurs et de testeurs pour réimplémenter chaque centimètre des deux langues, ainsi que leurs outils dans une énorme solution multi-projets avec des millions de lignes de code écrites dans les deux langues. Et compte tenu du grand nombre de développeurs qui alternent entre eux, du haut niveau de compatibilité avec les résultats et l'expérience des versions précédentes, ainsi que de la nécessité de reproduire de manière fiable le gigantesque volume d'API dans les moindres détails, j'ai été obligé de connaître de très près les différences. En fait, il me semblait parfois que j'apprenais chaque jour quelque chose de nouveau sur VB.NET (ma langue préférée).
Et enfin, j'ai pris le temps de m'asseoir et de décharger du cerveau une particule de ce que j'ai appris à utiliser et à créer VB.NET au cours des 15 dernières années, dans l'espoir de pouvoir au moins gagner du temps la prochaine fois.
Avant de passer à la liste, je décrirai les règles de base:
- Cette liste n'est pas exhaustive au sens habituel. Il est exhaustif. Ce ne sont pas toutes les différences. Ce ne sont même pas toutes les différences que je connais généralement. Ce ne sont que les différences dont je me souviens d'abord, jusqu'à ce que je sois trop fatigué pour continuer; jusqu'à ce que je manque de force. Si moi ou l'un d'entre vous rencontrez ou vous rappelez d'autres différences, je serai heureux de mettre à jour cette liste.
- Je vais commencer au début de la spécification VB 11 et descendre en utilisant son contenu pour me rappeler les différences qui me viennent à l'esprit en premier sur ce sujet.
- Ce n'est PAS une liste de fonctions en VB qui ne sont pas en C #. Donc pas de "littéraux XML contre des pointeurs". C'est trop banal, et il y a déjà des tonnes de telles listes sur Internet (dont certaines ont été écrites par moi, et peut-être à l'avenir j'écrirai plus). Je me concentrerai principalement sur les constructions qui ont un analogue dans les deux langues, et où un observateur non informé peut suggérer que ces deux choses se comportent de la même manière, mais où il y a de petites ou de grandes différences; ils peuvent se ressembler, mais fonctionnent différemment ou génèrent finalement un code différent.
- Ce n'est PAS une liste de différences syntaxiques entre VB et C # (qui sont innombrables). Je parlerai principalement des différences sémantiques (ce que les choses signifient), et non des différences syntaxiques (comment les choses sont écrites). Ainsi, aucun élément comme «VB ne commence les commentaires par», et C # utilise // »ou« en C # _ est un identifiant valide, mais pas en VB ». Mais je vais enfreindre cette règle pour plusieurs cas. Après tout, la première section de la spécification concerne les règles lexicales.
- Très souvent, je vais donner des exemples, et parfois je donnerai des raisons pour lesquelles le design pourrait aller dans un sens ou dans un autre. Certaines décisions de conception ont été prises sous mes yeux, mais la grande majorité a précédé mon époque, et je ne peux que deviner pourquoi elles ont été prises.
- Veuillez laisser un commentaire ou me tweeter ( @ThatVBGuy ) pour me faire part de vos différences préférées et / ou de celles que vous souhaitez en savoir plus.
Ayant défini les attentes et sans plus tarder ...
Table des matières
Texte masquéSyntaxe et prétraitement
Annonces, etc.
Instructions
Syntaxe et prétraitement
1. Les mots clés et les opérateurs VB peuvent utiliser des caractères pleine largeur
Certaines langues (je ne sais pas exactement combien, mais au moins dans certaines formes de chinois, japonais et coréen) utilisent des caractères pleine largeur. En bref, cela signifie que lorsque vous utilisez une police monospace (comme le font la plupart des programmeurs), le caractère chinois occupera deux fois plus d'espace horizontal que les caractères latins que nous avions l'habitude de voir à l'ouest. Par exemple:

Ici, j'ai une déclaration de variable écrite en japonais et une initialisation avec une chaîne également écrite en japonais. Selon le traducteur Bing, la variable est appelée "message d'accueil" et la ligne indique "Bonjour tout le monde!" Le nom de variable en japonais ne fait que 2 caractères, mais il occupe l'espace de 4 caractères demi-largeur que mon clavier donne normalement, comme le premier commentaire le montre. Il existe des versions pleine largeur des nombres et de tous les autres caractères ASCII imprimés qui ont la même largeur que les caractères japonais. Pour le démontrer, j'ai écrit un deuxième commentaire en utilisant les nombres pleine largeur «1» et «2». Ce ne sont pas des «1» et des «2» comme dans le premier commentaire. Il n'y a pas d'espaces entre les chiffres. Vous pouvez également voir que la taille des caractères n'est pas exactement large de 2 caractères, il y a un léger décalage. Cela est dû en partie au fait que ce programme mélange des caractères pleine largeur et demi-largeur sur une seule ligne et sur les trois lignes.
Les espaces sont demi-largeur, les caractères alphanumériques sont pleine largeur. Nous ne sommes pas des programmeurs sauf si nous sommes obsédés par l'alignement du texte. Et il me semble que si vous êtes chinois, japonais ou coréen (ou quelqu'un d'autre qui utilise des caractères de taille normale pour leur langue) et utilisez des identifiants ou des chaînes écrits dans leur langue maternelle, ces erreurs d'alignement mineures sont exaspérantes.
D'après ce que je comprends, en fonction de votre clavier japonais, le basculement entre les hiéroglyphes et le latin est facile, mais il est préférable d'utiliser des caractères latins pleine largeur. VB le prend en charge dans les mots clés, les espaces, les opérateurs et même les guillemets. Donc tout cela peut être écrit comme ceci:

Comme vous pouvez le voir, dans cette version, les mots clés, les espaces, les commentaires, les opérateurs et même les guillemets utilisent leurs versions en taille réelle. Dans le chaos a mis de l'ordre.
Oui Les Japonais utilisent VB. En fait, malgré la syntaxe similaire à la langue anglaise (et c'est peut-être pour cela), pour la plupart des utilisateurs VB que je vois sur les forums, l'anglais n'est pas la langue principale. Au cours de mon travail chez Microsoft, j'ai rencontré plusieurs fois le MVP japonais VB, au moins l'un d'eux apportait constamment des bonbons japonais. Si vous êtes un programmeur VB de Chine, du Japon ou de Corée (ou de tout autre pays qui utilise des caractères pleine largeur), veuillez écrire dans les commentaires. (Dans les commentaires à l'auteur, ils ont écrit que les Japonais essayaient partout dans le code d'utiliser ascii -
environ Per. )
Moment amusant: lorsque j'ai initialement mis en œuvre des lignes interpolées en VB, je (à ma honte) n'ai pas pris en compte la possibilité de crochets bouclés pleine largeur dans les lieux de substitution. Vladimir Reshetnikov ( @vreshetnikov ) a découvert et corrigé cette erreur, de sorte que la grande tradition de tolérance VB sur la largeur des caractères est restée valide.2. VB prend en charge les devis intelligents
D'accord, c'est, bien sûr, un peu, mais mérite d'être mentionné. Avez-vous déjà vu un exemple de code dans un document texte comme celui-ci:

Et après avoir copié l'exemple dans votre code, avez-vous constaté qu'aucune des citations (mises en surbrillance) ne fonctionnait, car Word remplace toutes les citations ASCII habituelles par des
“
citations intelligentes
”
?
Non. D'accord, je l'avais, mais seulement lorsque j'ai copié les exemples en C #. Dans VB, les guillemets intelligents sont des délimiteurs valides pour les chaînes (il est amusant que les guillemets russes
«»
ne fonctionnent pas - Env. Per.):

Ils fonctionnent également à l'intérieur de cordes, bien que peut-être d'une manière étrange. Si vous doublez les guillemets intelligents pour l'échappement, alors tout ce que vous obtenez au moment de l'exécution est simplement le guillemet simple ("stupide"). Cela peut sembler un peu étrange, mais néanmoins très pratique, car presque partout ailleurs, les citations intelligentes sont autorisées dans la chaîne. Le compilateur ne vous oblige PAS à terminer avec un devis intelligent ou à utiliser le bon si vous avez commencé avec un devis intelligent, vous pouvez donc le mélanger sans vous inquiéter. Et oui, cela fonctionne également avec le caractère de guillemet simple utilisé pour les commentaires:

J'ai essayé de faire admettre à Paul Wick (
@panopticoncntrl ) qu'il l'avait fait uniquement parce qu'il était tourmenté par ce problème pendant qu'il travaillait sur la spécification, mais il nie toute culpabilité. Ce n'était pas le cas dans VB6, donc quelqu'un l'a ajouté plus tard.
3. Les constantes de prétraitement peuvent être de tout type primitif (y compris les dates) et peuvent contenir n'importe quelle valeur constante

4. Les opérateurs arithmétiques peuvent être utilisés dans les expressions de prétraitement

Annonces, etc.
5. VB ignore parfois les implémentations IL pour empêcher l'implémentation implicite accidentelle d'une interface par son nom.
Cet article est de la catégorie de l'ésotérisme. En VB, l'implémentation d'une interface se fait toujours explicitement. Mais il s'avère qu'en l'absence d'une implémentation explicite, le comportement par défaut du CLR lors de l'appel d'une méthode d'interface est de rechercher des méthodes publiques par nom et signature. Dans la plupart des cas, cela est normal, car dans VB, vous devez
généralement fournir une implémentation pour chaque membre de l'interface que vous implémentez, à l'exception d'un cas:
Interface IFoo Sub Bar() Sub Baz() End Interface Class Foo Implements IFoo Private Sub Bar() Implements IFoo.Bar Exit Sub End Sub Private Sub IFoo_Baz() Implements IFoo.Baz Exit Sub End Sub End Class Class FooDerived Inherits Foo Implements IFoo Public Sub Bar() Implements IFoo.Bar Exit Sub End Sub Public Sub Baz()
gist.github.com/AnthonyDGreen/39634fd98a0cacc093719ab62d7ab1e6#file-partial-re-implementation-vbDans cet exemple, la classe
FooDerived
souhaite simplement réaffecter
IFoo.Bar
à la nouvelle méthode, mais laisse les implémentations restantes inchangées. Il s'avère que si le compilateur génère simplement la directive
FooDerived
pour
FooDerived
, le CLR récupérera également
FooDerived.Baz
tant que nouvelle implémentation de
IFoo.Baz
(bien que dans cet exemple, il ne soit pas lié à
IFoo
). En C #, cela se produit implicitement (et je ne suis pas sûr de pouvoir le refuser), mais en VB, le compilateur omet en fait `` Implements '' de la déclaration entière pour éviter cela, et redéfinit uniquement les membres spécifiques qui ont été réimplémentés. En d'autres termes, si vous demandez à
FooDerived
s'il
IFoo
directement, il répondra non:

Pourquoi est-ce que je le sais et pourquoi est-ce important? Depuis de nombreuses années, les utilisateurs de VB demandent un support pour l'implémentation implicite d'une interface (sans spécifier explicitement
Implements
dans chaque déclaration), généralement pour la génération de code. Le
FooDerived.Baz
fait d'incorporer cela à la syntaxe actuelle constituerait une rupture, car
FooDerived.Baz
désormais implicitement
IFoo.Baz
, bien qu'il ne l'ait encore jamais fait. Mais plus récemment, j'ai appris plus sur ce comportement en discutant des problèmes de conception potentiels avec la fonctionnalité «implémentation d'interface par défaut», qui permettrait aux interfaces d'inclure les implémentations par défaut de certains membres et de ne pas nécessiter de réimplémentation dans chaque classe. Cela serait utile pour les surcharges, par exemple, lorsque l'implémentation est susceptible d'être la même pour tous les implémenteurs (délégation à la surcharge principale). Un autre scénario est le versioning. Si une interface peut inclure des implémentations par défaut, vous pouvez y ajouter de nouveaux membres sans interrompre les anciennes implémentations. Mais il y a un problème. Étant donné que le comportement par défaut dans le CLR consiste à rechercher des implémentations publiques par nom et signature, si la classe VB n'implémente pas de membres d'interface avec des implémentations par défaut, mais a des membres publics avec un nom et une signature appropriés, ils implémentent implicitement ces membres d'interface, même si vous le faites complètement pas censé. Vous pouvez contourner ce problème lorsque l'ensemble des membres de l'interface est connu au moment de la compilation. Mais si le membre a été ajouté après la compilation du code, il modifie simplement le comportement en mode silencieux.
6. VB par défaut masque les membres de la classe de base par nom (Shadows), et non par nom et signature (Overloads)
Je pense que cette différence est assez bien connue. Le scénario est le suivant: vous
DomainObject
classe de base (
DomainObject
), éventuellement hors de votre contrôle, et déclarez une méthode avec un nom qui a du sens dans le contexte de votre classe, par exemple,
Print
:
Class DomainObject End Class Class Invoice Inherits DomainObject Public Sub Print(copies As Integer)
gist.github.com/AnthonyDGreen/863cfd1e7536fe8bda7cd145795eaf9f#file-shadows-example-vbLe fait qu'une facture puisse être imprimée est logique. Mais dans la prochaine version de l'API où votre classe de base est déclarée, ils décident pour le débogage d'ajouter à tous les DomainObjects une méthode qui affiche le contenu complet de l'objet dans la fenêtre de débogage. Cette méthode est brillamment appelée Print. Le problème est que le client de votre API peut remarquer que l'objet Facture a des méthodes
Print()
et
Print(Integer)
et penser que ce sont des surcharges liées. Peut-être que le premier imprime juste une copie. Mais ce n'est pas du tout ce que vous avez conçu comme l'auteur de Invoice. Vous ne
DomainObject.Print
pas que
DomainObject.Print
apparaîtrait. Alors oui, cela ne fonctionne pas en VB. Lorsque cette situation apparaît, un avertissement apparaît, mais plus important encore, le comportement par défaut dans VB est de se masquer par son nom. Autrement dit, jusqu'à ce que vous indiquiez explicitement avec le mot clé
Overloads
que votre
Print
est une surcharge de
Print
classe de base, un membre de la classe de base (et toute surcharge de celle-ci) sont complètement masqués. Seule l'API que vous avez déclarée à l'origine est affichée pour vos clients de classe. Cela fonctionne par défaut, mais vous pouvez le faire explicitement via le mot clé Shadows. C # ne peut faire que des
Overloads
(bien qu'il prenne en compte les
Shadows
lorsqu'il se réfère à une bibliothèque VB) et le fait par défaut (en utilisant le
new
mot-clé). Mais cette différence apparaît de temps en temps lorsque certaines hiérarchies d'héritage apparaissent dans des projets où une classe est définie dans une langue et une autre dans une autre, et il existe des méthodes surchargées, mais cela dépasse le cadre de l'élément actuel dans la liste des différences.
7. Les versions VB11 et inférieures sont plus strictes pour les membres protégés dans les génériques
En fait, nous avons changé cela entre VS2013 et VS2015. En particulier, nous avons décidé de ne pas nous soucier de la réimplémentation. Mais j'écris cette distinction au cas où vous utilisez l'ancienne version et que vous la remarquiez. En bref: si un membre protégé est déclaré dans le type générique, l'héritier, étant également générique, ne peut accéder à ce membre protégé que via l'instance héritée avec les mêmes arguments de type.
Class Base(Of T) Protected x As T End Class Class Derived(Of T) Inherits Base(Of T) Public Sub F(y As Derived(Of String))
gist.github.com/AnthonyDGreen/ce12ac986219eb51d6c85fa02c339a2f#file-protected-in-generics-vb8. La syntaxe «argument nommé» dans les attributs initialise toujours les propriétés / champs
VB utilise la même syntaxe
:=
pour initialiser les propriétés / champs d'attribut que pour passer des arguments de méthode par nom. Par conséquent, il n'existe aucun moyen de transmettre un argument au constructeur d'attribut par son nom.
9. Toutes les déclarations de niveau supérieur (généralement) sont implicitement situées dans l'espace de noms racine du projet
Cette différence est presque dans la catégorie «fonctionnalités avancées», mais je l'ai incluse dans la liste car elle change la signification du code. Il y a un champ dans les propriétés du projet VB:

Par défaut, il s'agit simplement du nom de votre projet au moment de sa création. Ce n'est
PAS le même champ que "l'espace de noms par défaut" dans les propriétés d'un projet C #. L'espace de noms par défaut définit simplement le code qui est ajouté par défaut aux nouveaux fichiers en C #. Mais l'espace de noms racine dans VB signifie que, sauf indication contraire, chaque déclaration de niveau supérieur dans ce projet se trouve implicitement dans cet espace de noms. C'est pourquoi les modèles de document VB ne contiennent généralement aucune déclaration d'espace de noms. De plus, si vous ajoutez une déclaration d'espace de noms, elle ne remplace pas la racine, mais y est ajoutée:
Namespace Controllers
gist.github.com/AnthonyDGreen/fd1e5e3a58aee862a5082e1d2b078084#file-root-namespace-vbAinsi, l'espace de noms
Controllers
déclare en fait l'
VBExamples.Controllers
noms
VBExamples.Controllers
, sauf si vous vous débarrassez de ce mécanisme en déclarant explicitement
Global
dans l'espace de noms.
C'est pratique car il enregistre un niveau d'indentation et un concept supplémentaire pour chaque fichier VB. Et cela est particulièrement utile si vous créez une application UWP (car tout doit être dans l'espace de noms dans UWP), et c'est extrêmement pratique si vous décidez de changer l'espace de noms de niveau supérieur pour l'ensemble de votre projet, par exemple, à partir d'un nom de code comme Roslyn en une version plus longue, comme
Microsoft.CodeAnalysis
, car vous n'avez pas à mettre à jour manuellement chaque fichier dans la solution. Il est également important de garder cela à l'esprit lorsque vous travaillez avec des générateurs de code, des espaces de noms XAML et le nouveau
.vbproj
fichier
.vbproj
.
10. Les modules ne sont pas générés en tant que classes abstraites scellées dans IL, ils ne ressemblent donc pas exactement aux classes statiques C # et vice versa.
Les modules en VB existaient avant les classes statiques C #, bien que nous ayons essayé en 2010 de les rendre identiques en termes d'IL. Malheureusement, il s'agissait d'un changement de rupture, car le sérialiseur XML (ou peut-être qu'il était binaire) pour cette version de .NET (je pense qu'ils l'ont corrigé) ne voulait pas sérialiser le type imbriqué dans un type qui ne pouvait pas être créé (et une classe abstraite ne peut pas). Il a levé une exception.
Nous avons trouvé cela après avoir apporté les modifications et les avons annulées car du code utilisait quelque part le type d'énumération incorporé dans le module. Et comme vous ne savez pas avec quelle version du sérialiseur le programme compilé fonctionnera, il ne sera jamais possible de le changer, car dans une version de l'application cela fonctionnera, et dans d'autres cas il lèvera des exceptions.
11. Vous n'avez pas besoin d'une méthode explicite pour le point d'entrée (Sub Main) dans les applications WinForms
Si votre projet utilise Form comme objet de départ et n'utilise pas le «Framework d'application» (plus à ce sujet dans le prochain article), VB génère un
Sub Main
, qui crée votre formulaire de démarrage et le transmet à
Application.Run
, vous permettant d'économiser le fichier entier Pour gérer ce processus, soit une méthode supplémentaire dans votre
Form
, soit même la nécessité de réfléchir à ce problème.
12. Si vous appelez des méthodes d'exécution VB obsolètes (par exemple, FileOpen), la méthode appelante sera implicitement marquée avec un attribut pour désactiver l'inline pour des raisons d'exactitude
En bref, les méthodes de travail avec des fichiers de style VB6 comme
FileOpen
reposent sur des contextes spécifiques à l'assembly où se trouve le code. Par exemple, le fichier # 1 peut être un journal dans un projet et une configuration dans un autre. Pour déterminer quel assembly est en cours d'exécution,
Assembly.GetCallingAssembly()
appelé. Mais si le JIT
insère votre méthode dans l'appelant, du point de vue de la pile, la méthode d'exécution VB ne sera pas appelée par votre méthode, mais par l'appelant, qui peut être dans un assembly différent, qui peut alors autoriser votre code à accéder ou à violer l'état interne de l'appelant objet. Ce n'est pas un problème de sécurité, car si le code compromettant s'exécute dans votre processus, vous avez déjà perdu. C'est une question d'exactitude. Par conséquent, si vous utilisez ces méthodes, le compilateur désactive l'inline.
Cette modification a été apportée au dernier moment en 2010, car le x64 JIT est TRÈS agressif lors de l'inlining / optimisation du code, et nous l'avons trouvé très tard, et c'était l'option la plus sûre.
13. Si votre type est marqué avec l'attribut DesignerGenerated et ne contient aucune déclaration de constructeur explicite, le compilateur généré par défaut appellera InitializeComponent s'il est défini pour ce type
À l'époque avant l'avènement des types partiels, l'équipe VB a mené une guerre pour réduire le code standard dans les projets WinForms. Mais même avec
Partial
cela est utile car il permet au fichier généré d'omettre complètement le constructeur, et l'utilisateur peut le déclarer manuellement dans son fichier si nécessaire, ou ne pas le déclarer sinon. Sans cela, le concepteur serait obligé d'ajouter un constructeur uniquement pour appeler
InitializeComponent
, et si l'utilisateur ajoute également, ils seront des doublons, ou la boîte à outils devrait être suffisamment intelligente pour déplacer le constructeur du fichier de concepteur vers l'utilisateur et ne pas le régénérer dans le concepteur, s'il l'a déjà existe dans le fichier utilisateur.
14. L'absence du modificateur Partiel ne signifie PAS que le type n'est pas partiel
Techniquement, en VB, une seule classe doit être marquée comme partielle. Il s'agit généralement (dans les projets GUI) d'un fichier généré.
Pourquoi? Cela conserve le fichier utilisateur beau et propre, et peut être très pratique à inclure après avoir généré ou ajouté au code utilisateur généré. Cependant, il est recommandé qu'au plus une classe n'ait pas de modificateur partiel, sinon un avertissement est émis.
15. Dans les classes par défaut, le niveau d'accès public est pour tout sauf les champs, et dans les structures publiques, pour les champs aussi
J'ai des sentiments mitigés à ce sujet. En C #, tout est
private
par défaut (acclamations, encapsulation!), Mais il y a un argument à faire en fonction de ce que vous déclarez souvent: un contrat public ou des détails d'implémentation. Les propriétés et les événements sont généralement destinés à un usage externe (
public
) et les opérateurs ne sont pas accessibles autrement que
public
. Cependant, je me fie rarement à l'accessibilité par défaut (à l'exception des démos comme les exemples de cet article).
16. VB initialise les champs APRÈS l'appel du constructeur de base, tandis que C # les initialise AVANT l'appel du constructeur de base
Avez-vous entendu comment "certains" disent que la première chose qui se produit dans le constructeur est un appel au constructeur de la classe de base? Eh bien, ce n'est pas le cas, du moins en C #. En C #, avant d'appeler
base()
, explicite ou implicite, les initialiseurs de champ sont exécutés en premier, puis l'appel du constructeur, puis votre code. Cette décision a des conséquences, et je pense que je sais pourquoi les développeurs de langage pourraient aller d'une manière ou d'une autre. Je crois que l'une de ces conséquences est que le code suivant ne peut pas être directement traduit en C #:
Imports System.Reflection Class ReflectionFoo Private StringType As Type = GetType(String) Private StringLengthProperty As PropertyInfo = StringType.GetProperty("Length") Private StringGetEnumeratorMethod As MethodInfo = StringType.GetMethod("GetEnumerator") Private StringEnumeratorType As Type = StringGetEnumeratorMethod.ReturnType Sub New() Console.WriteLine(StringType) End Sub End Class
gist.github.com/AnthonyDGreen/37d01c8e7f085e06172bfaf6a1e567d4#file-field-init-me-reference-vbÀ l'époque où j'étais engagé dans Reflection, j'écrivais souvent un tel code. Et je me souviens vaguement d'un collègue avant Microsoft (Josh), qui a traduit mon code en C #, se plaignant parfois de la nécessité de porter tous mes initialiseurs vers le constructeur. En C #, il est interdit de faire référence à un objet en cours de création avant l'appel de
base()
. Et puisque les initialiseurs de champ sont exécutés avant l'appel spécifié, ils ne peuvent pas non plus faire référence à d'autres champs ou à aucun membre de l'instance d'objet. Cet exemple ne fonctionne donc également qu'en VB:
MustInherit Class Base
gist.github.com/AnthonyDGreen/fe5ca89e5a98efee97ffee93aa684e50#file-base-derived-init-vbIci, nous avons une classe de base, qui a censément beaucoup de fonctionnalités, mais elle a besoin d'un objet clé pour la gestion, le travail, qui est déterminé par le type dérivé. Il existe de nombreuses façons de mettre en œuvre un tel modèle, mais j'utilise généralement celui-ci car:
- il est petit;
- ne m'oblige pas à déclarer un constructeur;
- ne nécessite pas que je mette le code d'initialisation dans le constructeur, le cas échéant;
- me permet de mettre en cache l'objet créé et ne nécessite pas de types dérivés pour déclarer et gérer le stockage de l'objet fourni, bien que ce ne soit plus le cas avec les propriétés automatiques.
De plus, je me suis retrouvé dans les deux situations: lorsqu'un champ dans un type dérivé voulait appeler une méthode déclarée dans la classe de base, et lorsque l'initialiseur de classe de champ devait appeler le membre
MustOverride
implémenté par le type dérivé. Les deux sont valides en VB et aucun en C #, et cela a du sens. Si l'initialiseur de champ C # pouvait appeler un membre de la classe de base, ce membre pourrait dépendre des champs initialisés dans le constructeur de base (qui n'est pas déjà en cours d'exécution) et les résultats seraient presque certainement faux, et il n'y avait aucun moyen de contourner cela.
Mais en VB, le constructeur de base a déjà fonctionné, vous pouvez donc tout faire! Dans le cas contraire, tout est un peu plus compliqué, car appeler le membre
Overridable
depuis l'initialiseur (ou constructeur) de la classe de base peut conduire à accéder aux champs avant qu'ils ne soient «initialisés». Mais seule votre implémentation sait si c'est un problème. Dans mes scripts, cela ne se produit tout simplement pas. Ils ne dépendent pas de l'état de l'instance, mais ne peuvent pas être des membres
Shared
car vous ne pouvez pas avoir de membre
Shared Overridable
dans n'importe quelle langue pour des raisons techniques dépassant le cadre de cet article. De plus, il est clairement défini ce qui arrive aux champs avant de démarrer les initialiseurs personnalisés - ils sont initialisés avec des valeurs par défaut, comme toutes les variables dans VB. Pas de surprise.
Alors pourquoi? En fait, je ne sais pas si mes scripts étaient ce que l'équipe originale de VB.NET avait en tête quand ils l'ont conçu. Cela fonctionne juste dans mon cas! , : VB , , . . .
, , , C# VB, , VB , C#.17. (backing field) VB , C#,
( ).
E
, VB ( IDE)
EEvent
. C#
E
, ,
E
, .
18. VB
P
,
_P'
. IntelliSense, . C# «» (
mangled ) , , C# .
? VB , -, «WithEvents», -, , - , .
19. read-only
, , …. VB « » .
WithEvents
-, non-Custom , , . IntelliSense, , , . FTW! , VB , private set; C#.
Class Alarm Private ReadOnly Code As Integer ReadOnly Property Status As String = "Disarmed" Sub New(code As Integer) Me.Code = code End Sub Sub Arm()
gist.github.com/AnthonyDGreen/57ce7962700c5498894ad417296f9066#file-read-only-auto-property-backing-field-is-writeable-vb20.
,
NonSerialized
.
VB (expanded) Custom- 2005 (?)
, , ,
NonSerialized
. , , , , «» , « ».
, , , , . , , , , two-way bindable ( ,
PropertyChanged
), , , , , .
, , CLSA «Expert Business Objects»
(Rocky Lhotka) , undo/redo ( , , - , , ), . , . , , , .
Instructions
21. — , ; ( )
,
GoTo
. , - . ,
For
For Each
;
Using
,
SyncLock
With
, , ,
Finally
.
If
Select Case
,
Do
While
, Try — , :
Module Program Sub Main() Dim retryCount = 0 Try Retry:
gist.github.com/AnthonyDGreen/b93adcf3c3705e4768dcab0b05b187a0#file-try-goto-retry-vb, , , .NET VB «». VB6 Quick Basic ( ) . QB, . , « », . GoTo, — , .
:
Try
, VB -
await
Catch
Finally
, ,
GoTo
.
22. <>
, VB ( ) (
static
) ( ). , .
Catch
3 .
Try
Catch
, , ,
Try
.
, VB.NET , . CLR VB . : , .
, C# , , «». VB.NET .23.
, , C# « » (
definite assignment ). , , , « ». , ( ) , , . C/C++. , ! , . , , , — . , , , , , , , , . , BASIC , , «» ,
= Nothing
,
= 0
,
= False
..
, (
flow analysis ) VB , .
, C# , , , . VB
, , , . Roslyn, , API « », , .
24. RaiseEvent , null
, - C# VB.
RaiseEvent
VB — ,
null
( ),
null
- — , .
gist.github.com/AnthonyDGreen/c3dea3d91ef4ffc50cfa92c41f967937#file-null-safe-event-raising-vb, null-conditional C# VS2015 C# , VB ( ), , ; VB.NET .
25. ; (shallow clone)
, , 17 , , . (boxed) Object,
System.Runtime.CompilerServices.RuntimeHelper.GetObjectValue
. , CLR. , :
, , , , (
late-bound situations ). , , ( ) , , , , (
caller's copy ). , , - , — .
. :
Class MyEventArgs Property Value As Object End Class Structure MyStruct Public X, Y As Integer End Structure Module Program Sub Main() Dim defaultValue As Object = New MyStruct With {.X = 3, .Y = 5} Dim e = New MyEventArgs With {.Value = defaultValue} RaiseEvent DoSomething(Nothing, e) If e.Value Is defaultValue Then
gist.github.com/AnthonyDGreen/422ac4574af92d9bbbf59f0fbc40b74d#file-get-object-value-vb, WPF, . , . , , . , . , - , , , .
, , « » . IronRuby/Python,
dynamic
C# ( C#): C#
GetObjectValue
.
object.ReferenceEquals
, , ,
-
dynamic
C# ( ).
==
, . C#, , .
26. Select Case «» (fall-through); break
Friday , Sunday — , 5 .
Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday: Case DayOfWeek.Tuesday: Case DayOfWeek.Wednesday: Case DayOfWeek.Thursday: Case DayOfWeek.Friday: Console.WriteLine("Weekday") Case DayOfWeek.Saturday: Case DayOfWeek.Sunday: Console.WriteLine("Weekend") End Select End Sub End Module
gist.github.com/AnthonyDGreen/7b7e136c71dd11b2417a6c7267bb3546#file-select-case-no-fallthrough-vbRoslyn C# , - :
«, ? !» «, » . . VS , , , , , . !
. C# , C, C. . , C# , case . - ,
goto
,
break
. VB
break
,
Exit Select
, , VB .
27. Case
, . C#, :
Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday Dim message = "Get to work!" Case DayOfWeek.Saturday, DayOfWeek.Sunday Dim message = "Funtime!" End Select End Sub End Module
gist.github.com/AnthonyDGreen/bd642061896246c9336255881fb78546#file-select-case-scopes-vb,
message
, C#
switch
case
— . . , , - ( , C): , , , , .
28, 29, 30. Select Case , =
, , , ,
Select Case
.
, , . :
Select Case
— , , …
switch
— / , « ».
, 26-30.
switch
, , , ,
if
. IL
switch
, ,
If
, VB , . switch , , C . VB .
31. , ,
x
, , -1, -2, -3:
Module Program Sub Main() For i = 1 To 3 Dim x As Integer x -= 1 Console.WriteLine(x) Next End Sub End Module
gist.github.com/AnthonyDGreen/cbc3a9c70677354973d64f1d993a3c5d#file-loop-variables-retain-their-values-vb« , , » ( ). , VB2008 , -:
Module Program Sub Main() Dim lambdas = New List(Of Action) For i = 1 To 3 Dim x As Integer x -= 1 lambdas.Add(Sub() Console.WriteLine(x)) Next For Each lambda In lambdas lambda() Next End Sub End Module
gist.github.com/AnthonyDGreen/2ef9ba3dfcf9a1abe0e94b0cde12faf1#file-loop-variables-captured-per-iteration-vb-1, -2, -3.
x
— « », -
x
, . ,
x
. flow analysis API — ! (
«… … ?» )
Pourquoi? , , , , , #22. , , -, .
, VB C# (
control variables )
For Each
VS2012 (?), - « ». 10000% , ( , VB , ). , VB
For
, . , . , VB
For
For Each
,
for
foreach
C#. ,
For
VB - , , .
32. For
For
. , , 1,3,5,7,9, , .
Module Program Sub Main() Dim lower = 1, upper = 9, increment = 2 For i = lower To upper Step increment Console.WriteLine(i) upper += 1 increment -= 1 Next End Sub End Module
gist.github.com/AnthonyDGreen/1e48113be204f515c51e221858666ac7#file-for-loop-bounds-cached-vb, ( ), , , ,
IndexOutOfRangeExceptions
, .
, , , , , C, VB . - , VB ,
For i = a To b Step c
( ,
i> b
) ( ,
i <b
),
c
? , , ,
b
, — . , , , , .
33. For Each VB GetEnumerator
For Each
,
IEnumerable
,
GetEnumerator
,
For Each
.
, ,
For Each
IEnumerator
, , :
Module Program Sub Main() Dim list = New List(Of Integer) From {1, 2, 3, 4, 5} Dim info = list.FirstAndRest() If info.First IsNot Nothing Then Console.Write(info.First.GetValueOrDefault()) For Each other In info.Additional Console.Write(", ") Console.Write(other) Next Console.WriteLine() End If End Sub <Runtime.CompilerServices.Extension> Function FirstAndRest(Of T As Structure)(sequence As IEnumerable(Of T)) As (First As T?, Additional As IEnumerator(Of T)) Dim enumerator = sequence.GetEnumerator() If enumerator.MoveNext() Then Return (enumerator.Current, enumerator) Else Return (Nothing, enumerator) End If End Function <Runtime.CompilerServices.Extension> Function GetEnumerator(Of T)(enumerator As IEnumerator(Of T)) As IEnumerator(Of T) Return enumerator End Function End Module
gist.github.com/AnthonyDGreen/d7dbb7a5b98a940765c4adc33e3eaeee#file-for-each-extension-get-enumerator-vbF# ,
IEnumerator
,
For Each
, .
VB , (
well-known name ), . , , Add, . C# , (.
async
/
await
). , C# Roslyn () , .
Minute de publicité. 15-16 - .NET- DotNext 2019 Piter . , . , . .