Eine vollständige Liste der Unterschiede zwischen VB.NET und C #. Teil 1

Bild

Laut dem TIOBE- Rating von 2018 hat VB.NET die Popularität von C # übertroffen. Zufall oder nicht, aber im Februar forderte Eric Lippert, einer der Schöpfer von C #, die Leser auf, auf den Blog seines Freundes, eines ehemaligen Teamkollegen des Roslyn-Compilers und in Kombination eines begeisterten VB.NET-Fans, Anthony Green , zu achten. "Solche Ressourcen sind detaillierte Details von Experten, die beim Lesen der Dokumentation nicht so leicht zu finden sind", schreibt Eric. Wir präsentieren Ihnen den ersten Teil der Übersetzung von Anthony Green's Artikel "Eine vollständige Liste der Unterschiede zwischen VB.NET und C #". Vielleicht liegt genau in diesen Unterschieden das Geheimnis der Dynamik der Bewertung dieser Sprachen.

Fast die Hälfte meines Lebens habe ich unzählige Diskussionen darüber miterlebt und daran teilgenommen, wie ähnlich oder unterschiedlich die beiden beliebtesten .NET-Sprachen sind. Zuerst als Amateur, dann als Profi und schließlich als Kundenanwalt, Programmmanager und Sprachdesigner kann ich ohne Übertreibung sagen, wie oft ich so etwas gehört oder gelesen habe:
"... VB.NET ist wirklich nur eine dünne Schicht auf IL, wie C # ..."
oder
"... VB.NET ist eigentlich nur C # ohne Semikolons ..."
Als ob die Sprachen eine XML-Konvertierung oder ein Stylesheet wären.

Und wenn ein begeisterter Besucher dies nicht in einen Kommentar schreibt, wird dies häufig in Fragen wie „Hallo, Anthony! Ich bin an einem einzigen Ort auf einen so kleinen Unterschied gestoßen - ist das ein Fehler? Wie könnten sich diese beiden ansonsten identischen Sprachen, die im Namen aller Guten und Heiligen dieser Welt identisch sein sollten, an diesem einen Ort zerstreuen? Warum brauchen wir solche Ungerechtigkeit ?!

" Getrennt ", als wären sie bis zu einer Mutation gleich und würden dann zu getrennten Arten. Hah!

Aber das verstehe ich. Bevor ich zu Microsoft kam, habe ich mich vielleicht auch vage an diese Idee gehalten und sie als Argument verwendet, um auf Gegner zu reagieren oder jemanden zu beruhigen. Ich verstehe ihren Charme. Es ist leicht zu verstehen und sehr leicht zu wiederholen. Aber als ich 5 Jahre lang an Roslyn arbeitete (VB und C # komplett neu geschrieben), wurde mir klar, wie eindeutig falsch diese Idee ist . Ich habe mit einem Team von Entwicklern und Testern zusammengearbeitet, um jeden Zentimeter beider Sprachen sowie deren Tools in einer riesigen Multiprojektlösung mit Millionen von Codezeilen, die in beiden Sprachen geschrieben sind, neu zu implementieren. Angesichts der großen Anzahl von Entwicklern, die zwischen ihnen hin und her wechseln, der hohen Kompatibilität mit den Ergebnissen und Erfahrungen früherer Versionen sowie der Notwendigkeit, das gigantische API-Volumen zuverlässig und detailliert zu reproduzieren, musste ich die Unterschiede sehr genau kennenlernen. Tatsächlich schien es mir manchmal, dass ich jeden Tag etwas Neues über VB.NET (meine Lieblingssprache) gelernt habe.

Und schließlich nahm ich mir die Zeit, mich hinzusetzen und ein Teil dessen, was ich in den letzten 15 Jahren mit VB.NET gelernt und erstellt habe, aus dem Gehirn zu entfernen, in der Hoffnung, dass ich beim nächsten Mal zumindest meine Zeit sparen kann.

Bevor ich zur Liste übergehe, werde ich die Grundregeln skizzieren:

  • Diese Liste ist im üblichen Sinne nicht vollständig. Er ist erschöpfend. Dies sind nicht alle Unterschiede. Dies sind nicht einmal alle Unterschiede, die ich allgemein kenne. Dies sind nur die Unterschiede, an die ich mich zuerst erinnern kann, bis ich zu müde werde, um fortzufahren. bis mir die Kraft ausgeht. Wenn ich oder einer von Ihnen auf andere Unterschiede stößt oder sich daran erinnert, werde ich diese Liste gerne aktualisieren.
  • Ich werde am Anfang der VB 11-Spezifikation beginnen und mich anhand des Inhalts nach unten bewegen, um mich an die Unterschiede zu erinnern, die mir bei diesem Thema zuerst einfallen.
  • Dies ist KEINE Liste von Funktionen in VB, die nicht in C # enthalten sind. Also keine "XML-Literale versus Zeiger". Dies ist zu alltäglich, und es gibt bereits Unmengen solcher Listen im Internet (von denen einige von mir geschrieben wurden, und vielleicht werde ich in Zukunft mehr schreiben). Ich werde mich hauptsächlich auf Konstruktionen konzentrieren, die in beiden Sprachen ein Analogon haben und bei denen ein nicht informierter Beobachter vorschlagen kann, dass sich diese beiden Dinge gleich verhalten, bei denen es jedoch kleine oder große Unterschiede gibt. Sie sehen zwar gleich aus, funktionieren jedoch anders oder generieren letztendlich unterschiedlichen Code.
  • Dies ist KEINE Liste syntaktischer Unterschiede zwischen VB und C # (die unzählig sind). Ich werde hauptsächlich über semantische Unterschiede sprechen (was Dinge bedeuten) und nicht über syntaktische Unterschiede (wie Dinge geschrieben werden). Also keine Teile wie "VB startet Kommentare mit 'und C # verwendet //" oder "in C # _ ist eine gültige Kennung, aber nicht in VB". Aber ich werde diese Regel in mehreren Fällen brechen. Schließlich geht es im ersten Abschnitt der Spezifikation um lexikalische Regeln.
  • Sehr oft werde ich Beispiele nennen und manchmal begründen, warum Design in die eine oder andere Richtung gehen könnte. Einige Designentscheidungen wurden vor meinen Augen getroffen, aber die überwiegende Mehrheit ging meiner Zeit voraus, und ich kann nur raten, warum sie getroffen wurden.
  • Bitte hinterlassen Sie einen Kommentar oder twittern Sie mich ( @ThatVBGuy ), um mir Ihre Lieblingsunterschiede und / oder diejenigen mitzuteilen , über die Sie mehr wissen möchten.

Erwartungen definiert und ohne weitere Verzögerungen ...

Inhalt


Versteckter Text

Syntax und Vorverarbeitung



Ankündigungen usw.



Anleitung



Syntax und Vorverarbeitung


1. VB-Schlüsselwörter und -Operatoren können Zeichen in voller Breite verwenden


Einige Sprachen (ich weiß nicht genau, wie viele, aber zumindest in einigen Formen von Chinesisch, Japanisch und Koreanisch) verwenden Zeichen in voller Breite. Kurz gesagt bedeutet dies, dass bei Verwendung einer Monospace-Schriftart (wie die meisten Programmierer) das chinesische Schriftzeichen doppelt so viel horizontalen Platz einnimmt wie die lateinischen Schriftzeichen, die wir im Westen gesehen haben. Zum Beispiel:



Hier habe ich eine Variablendeklaration in Japanisch und eine Initialisierung mit einer Zeichenfolge, die ebenfalls in Japanisch geschrieben ist. Laut dem Bing-Übersetzer heißt die Variable "Begrüßung" und in der Zeile steht "Hallo Welt!" Der Variablenname auf Japanisch ist nur 2 Zeichen lang, nimmt jedoch den Platz von 4 Zeichen halber Breite ein, den meine Tastatur normalerweise ausgibt, wie der erste Kommentar zeigt. Es gibt Versionen von Zahlen in voller Breite und alle anderen gedruckten ASCII-Zeichen, die dieselbe Breite wie japanische haben. Um dies zu demonstrieren, schrieb ich einen zweiten Kommentar mit den Zahlen „1“ und „2“ in voller Breite. Dies sind nicht solche "1" und "2" wie im ersten Kommentar. Zwischen den Zahlen stehen keine Leerzeichen. Sie können auch sehen, dass die Größe der Zeichen nicht genau 2 Zeichen breit ist, es gibt einen leichten Versatz. Dies liegt zum Teil daran, dass dieses Programm Zeichen voller und halber Breite in einer Zeile und in allen drei Zeilen mischt.

Leerzeichen haben die halbe Breite, alphanumerische Zeichen die volle Breite. Wir sind keine Programmierer, es sei denn, wir sind von der Textausrichtung besessen. Und es scheint mir, dass diese geringfügigen Ausrichtungsfehler ärgerlich sind, wenn Sie Chinesisch, Japanisch oder Koreanisch sind (oder jemand anderes, der Zeichen in voller Größe für seine Sprache verwendet) und Bezeichner oder Zeichenfolgen verwenden, die in ihrer Muttersprache geschrieben sind.

Soweit ich weiß, ist es je nach japanischer Tastatur einfach, zwischen Hieroglyphen und Latein zu wechseln. Es ist jedoch vorzuziehen, lateinische Zeichen in voller Breite zu verwenden. VB unterstützt dies in Schlüsselwörtern, Leerzeichen, Operatoren und sogar Anführungszeichen. Das alles kann also so geschrieben werden:



Wie Sie sehen können, verwenden in dieser Version Schlüsselwörter, Leerzeichen, Kommentare, Operatoren und sogar Anführungszeichen ihre Vollversionen. Ins Chaos brachte Ordnung.

Ja Die Japaner benutzen VB. Trotz der Syntax ähnlich der englischen Sprache (und vielleicht deshalb) ist Englisch für die meisten VB-Benutzer, die ich in den Foren sehe, nicht die Hauptsprache. Während meiner Arbeit bei Microsoft traf ich mehrmals den japanischen VB MVP, von denen mindestens einer ständig japanische Süßigkeiten mitbrachte. Wenn Sie ein VB-Programmierer aus China, Japan oder Korea sind (oder aus einem anderen Land, das Zeichen in voller Breite verwendet), schreiben Sie bitte in die Kommentare. (In den Kommentaren an den Autor haben sie geschrieben, dass die Japaner überall im Code versuchen, ASCII zu verwenden - ca. Per. )

Lustiger Moment: Als ich anfänglich interpolierte Linien in VB implementierte, habe ich (zu meiner Schande) die Möglichkeit von geschweiften Klammern in voller Breite an den Substitutionsstellen nicht berücksichtigt. Vladimir Reshetnikov ( @vreshetnikov ) entdeckte und korrigierte diesen Fehler, so dass die große Tradition der VB-Toleranz für Zeichenbreiten weiterhin gültig war.

2. VB unterstützt intelligente Anführungszeichen


Okay, das ist natürlich eine Kleinigkeit, aber erwähnenswert. Haben Sie jemals Beispielcode in einem Textdokument wie diesem gesehen:



Und nachdem Sie das Beispiel in Ihren Code kopiert haben, haben Sie festgestellt, dass keines der (hervorgehobenen) Anführungszeichen funktioniert hat, weil Word alle üblichen ASCII-Anführungszeichen durch intelligente Anführungszeichen ?

Ich nicht. Okay, ich hatte es, aber nur, als ich die Beispiele nach C # kopierte. In VB sind intelligente Anführungszeichen gültige Trennzeichen für Zeichenfolgen (es ist lustig, dass die russischen Anführungszeichen «» nicht funktionieren - ca. Per.):



Sie arbeiten auch in Saiten, wenn auch vielleicht auf seltsame Weise. Wenn Sie die cleveren Anführungszeichen für die Flucht verdoppeln, erhalten Sie zur Laufzeit nur das einfache („dumme“) Anführungszeichen. Dies mag etwas seltsam erscheinen, ist aber dennoch sehr praktisch, da fast überall in der Zeichenfolge intelligente Anführungszeichen zulässig sind. Der Compiler lässt Sie mit Sicherheit NICHT mit einem intelligenten Zitat enden oder das richtige verwenden, wenn Sie mit einem intelligenten Zitat begonnen haben, sodass Sie es ohne Sorgen verwechseln können. Und ja, dies funktioniert auch mit dem einfachen Anführungszeichen, das für Kommentare verwendet wird:



Ich habe versucht, Paul Wick ( @panopticoncntrl ) dazu zu bringen, zuzugeben, dass er dies nur getan hat, weil er bei der Arbeit an der Spezifikation mit diesem Problem gequält wurde, aber er bestreitet jede Schuld. Dies war in VB6 nicht der Fall, daher fügte dies später jemand hinzu.

3. Die Vorverarbeitungskonstanten können von einem beliebigen primitiven Typ (einschließlich Datumsangaben) sein und einen beliebigen konstanten Wert enthalten



4. Arithmetische Operatoren können zur Vorverarbeitung von Ausdrücken verwendet werden




Ankündigungen usw.


5. VB überspringt manchmal IL implementiert Implementierungen, um eine versehentliche implizite Implementierung einer Schnittstelle nach Namen zu verhindern.


Dieser Artikel gehört zur Kategorie der Esoterik. In VB erfolgt die Implementierung einer Schnittstelle immer explizit. Es stellt sich jedoch heraus, dass in Ermangelung einer expliziten Implementierung das Standardverhalten der CLR beim Aufrufen einer Schnittstellenmethode darin besteht, nach öffentlichen Methoden nach Name und Signatur zu suchen. In den meisten Fällen ist dies normal, da Sie in VB normalerweise für jedes Mitglied der von Ihnen implementierten Schnittstelle eine Implementierung bereitstellen müssen, mit Ausnahme eines Falls:

 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() ' Does something unrelated to what an IFoo.Baz would do. End Sub End Class 

gist.github.com/AnthonyDGreen/39634fd98a0cacc093719ab62d7ab1e6#file-partial-re-implementation-vb

In diesem Beispiel möchte die FooDerived Klasse FooDerived nur der neuen Methode neu IFoo.Bar , die verbleibenden Implementierungen jedoch unverändert lassen. Es stellt sich heraus, dass, wenn der Compiler einfach die Implementierungsanweisung für FooDerived , die CLR auch FooDerived.Baz als neue Implementierung von IFoo.Baz (obwohl es in diesem Beispiel nicht mit IFoo ). In C # geschieht dies implizit (und ich bin nicht sicher, ob ich es ablehnen kann), aber in VB lässt der Compiler 'Implements' in der gesamten Deklaration weg, um dies zu vermeiden, und überschreibt nur bestimmte Mitglieder, die erneut implementiert wurden. Mit anderen Worten, wenn Sie FooDerived fragen, ob IFoo direkt IFoo wird, wird Nein gesagt:


Warum weiß ich das und warum ist es wichtig? Seit vielen Jahren fordern VB-Benutzer Unterstützung für die implizite Implementierung einer Schnittstelle (ohne explizite Angabe von Implements in jeder Deklaration), normalerweise für die Codegenerierung. Nur dies in die aktuelle Syntax zu integrieren, würde die Änderung brechen, da FooDerived.Baz jetzt implizit IFoo.Baz implementiert, obwohl dies zuvor noch nicht geschehen ist. In jüngerer Zeit habe ich jedoch mehr über dieses Verhalten erfahren, als ich potenzielle Entwurfsprobleme mit der Funktion "Standardschnittstellenimplementierung" erörtert habe, mit der Schnittstellen Standardimplementierungen einiger Mitglieder enthalten können und nicht in jeder Klasse erneut implementiert werden müssen. Dies ist beispielsweise bei Überlastungen hilfreich, wenn die Implementierung wahrscheinlich für alle Implementierer gleich ist (Delegierung an die Hauptüberlastung). Ein anderes Szenario ist die Versionierung. Wenn eine Schnittstelle Standardimplementierungen enthalten kann, können Sie neue Mitglieder hinzufügen, ohne alte Implementierungen zu beschädigen. Aber es gibt ein Problem. Da das Standardverhalten in der CLR darin besteht, nach öffentlichen Implementierungen nach Name und Signatur zu suchen, implementieren die VB-Klasse diese Schnittstellenmitglieder implizit, auch wenn Sie dies vollständig tun, wenn die VB-Klasse keine Schnittstellenmitglieder mit Standardimplementierungen implementiert, sondern öffentliche Mitglieder mit einem geeigneten Namen und einer geeigneten Signatur soll nicht. Es gibt Dinge, die Sie tun können, um dies zu umgehen, wenn der vollständige Satz von Schnittstellenmitgliedern zur Kompilierungszeit bekannt ist. Falls das Mitglied jedoch nach dem Kompilieren des Codes hinzugefügt wurde, ändert es das Verhalten zur Laufzeit nur stillschweigend.

6. VB verbirgt standardmäßig Mitglieder der Basisklasse nach Namen (Shadows) und nicht nach Name und Signatur (Overloads).


Ich denke, dieser Unterschied ist ziemlich bekannt. Das Szenario lautet wie folgt: Sie erben die Basisklasse ( DomainObject ), möglicherweise außerhalb Ihrer Kontrolle, und deklarieren eine Methode mit einem Namen, der im Kontext Ihrer Klasse sinnvoll ist, z. B. Print :

 Class DomainObject End Class Class Invoice Inherits DomainObject Public Sub Print(copies As Integer) ' Sends contents of invoice to default printer. End Sub End Class 

gist.github.com/AnthonyDGreen/863cfd1e7536fe8bda7cd145795eaf9f#file-shadows-example-vb

Die Tatsache, dass eine Rechnung gedruckt werden kann, ist sinnvoll. In der nächsten Version der API, in der Ihre Basisklasse deklariert ist, entscheiden sie sich für das Debuggen, um allen DomainObjects eine Methode hinzuzufügen, die den vollständigen Inhalt des Objekts im Debug-Fenster anzeigt. Diese Methode wird hervorragend als Drucken bezeichnet. Das Problem besteht darin, dass der Client Ihrer API möglicherweise feststellt, dass das Invoice-Objekt über Print() und Print(Integer) -Methoden verfügt, und dass dies verwandte Überladungen sind. Vielleicht druckt der erste nur eine Kopie. Dies ist jedoch keineswegs das, was Sie als Rechnungsautor gedacht haben. Sie hatten keine Ahnung, dass DomainObject.Print . Ja, das funktioniert in VB nicht. Wenn diese Situation auftritt, wird eine Warnung angezeigt. Noch wichtiger ist jedoch, dass das Standardverhalten in VB darin besteht, sich nach Namen auszublenden. Das heißt, bis Sie mit dem Schlüsselwort Overloads explizit angeben, dass Ihr Print eine Überladung von Print Basisklasse ist, werden ein Mitglied der Basisklasse (und alle Überladungen davon) vollständig ausgeblendet. Nur die API, die Sie ursprünglich deklariert haben, wird Ihren Klassenclients angezeigt. Dies funktioniert standardmäßig, Sie können dies jedoch explizit über das Schlüsselwort Shadows tun. C # kann nur Overloads (obwohl es Shadows berücksichtigt, wenn es auf eine VB-Bibliothek verweist) und dies standardmäßig (unter Verwendung des new Schlüsselworts). Dieser Unterschied tritt jedoch von Zeit zu Zeit auf, wenn einige Vererbungshierarchien in Projekten angezeigt werden, in denen eine Klasse in einer Sprache und eine andere in einer anderen definiert ist, und es überladene Methoden gibt, die jedoch den Rahmen des aktuellen Elements in der Liste der Unterschiede sprengen.

7. VB11 und niedriger sind für geschützte Mitglieder in Generika strenger


Tatsächlich haben wir dies zwischen VS2013 und VS2015 geändert. Insbesondere haben wir uns entschlossen, uns nicht um die Neuimplementierung zu kümmern. Aber ich schreibe diese Unterscheidung, falls Sie die alte Version verwenden und es bemerken. Kurz gesagt: Wenn ein geschütztes Mitglied im generischen Typ deklariert ist, kann der Erbe, der auch generisch ist, nur über die geerbte Instanz mit denselben Typargumenten auf dieses geschützte Mitglied zugreifen.

 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)) ' Error: Derived(Of T) cannot access Derived(Of String)'s ' protected members yx = "a" End Sub End Class 

gist.github.com/AnthonyDGreen/ce12ac986219eb51d6c85fa02c339a2f#file-protected-in-generics-vb

8. Die Syntax "Named argument" in Attributen initialisiert immer Eigenschaften / Felder


VB verwendet dieselbe Syntax := zum Initialisieren von Attributeigenschaften / -feldern wie zum Übergeben von Methodenargumenten nach Namen. Daher gibt es keine Möglichkeit, ein Argument namentlich an den Attributkonstruktor zu übergeben.

9. Alle Deklarationen der obersten Ebene befinden sich (normalerweise) implizit im Stammnamensraum des Projekts


Dieser Unterschied liegt fast in der Kategorie "Erweiterte Funktionen", aber ich habe ihn in die Liste aufgenommen, da er die Bedeutung des Codes ändert. In den Eigenschaften des VB-Projekts befindet sich ein Feld:


Standardmäßig ist dies einfach der Name Ihres Projekts zum Zeitpunkt der Erstellung. Dies ist NICHT dasselbe Feld wie der "Standard-Namespace" in den Eigenschaften eines C # -Projekts. Der Standard-Namespace legt einfach fest, welcher Code standardmäßig neuen Dateien in C # hinzugefügt wird. Der Root-Namespace in VB bedeutet jedoch, dass, sofern nicht anders angegeben, jede Deklaration der obersten Ebene in diesem Projekt implizit in diesem Namespace enthalten ist. Aus diesem Grund enthalten VB-Dokumentvorlagen normalerweise keine Namespace-Deklarationen. Wenn Sie eine Namespace-Deklaration hinzufügen, wird der Stamm nicht überschrieben, sondern hinzugefügt:

 Namespace Controllers ' Child namespace. End Namespace Namespace Global.Controllers ' Top-level namespace End Namespace 

gist.github.com/AnthonyDGreen/fd1e5e3a58aee862a5082e1d2b078084#file-root-namespace-vb

Daher deklariert der Controllers Namespace tatsächlich den VBExamples.Controllers Namespace, es sei denn, Sie VBExamples.Controllers diesen Mechanismus, indem Sie Global explizit im Namespace deklarieren.

Dies ist praktisch, da für jede VB-Datei eine Einrückungsstufe und ein zusätzliches Konzept gespeichert werden. Dies ist besonders nützlich, wenn Sie eine UWP-Anwendung erstellen (da sich in UWP alles im Namespace befinden muss), und es ist äußerst praktisch, wenn Sie den Namespace der obersten Ebene für Ihr gesamtes Projekt beispielsweise von einem Codenamen wie Roslyn in ändern möchten eine längere Version wie Microsoft.CodeAnalysis , da Sie nicht jede Datei in der Lösung manuell aktualisieren müssen. Es ist auch wichtig, dies zu berücksichtigen, wenn Sie mit Codegeneratoren, XAML-Namespaces und dem neuen .vbproj Dateiformat arbeiten.

10. Module werden in IL nicht als versiegelte abstrakte Klassen generiert, sodass sie nicht genau wie statische C # -Klassen aussehen und umgekehrt.


Module in VB existierten vor den statischen C # -Klassen, obwohl wir 2010 versucht haben, sie in Bezug auf IL gleich zu machen. Leider war dies eine bahnbrechende Änderung, da der XML-Serializer (oder möglicherweise eine Binärdatei) für diese Version von .NET (ich glaube, sie haben ihn behoben) den verschachtelten Typ nicht in einen Typ serialisieren wollte, der nicht erstellt werden konnte (und eine abstrakte Klasse) kann nicht). Er warf eine Ausnahme.

Wir haben dies gefunden, nachdem wir die Änderungen vorgenommen und zurückgesetzt haben, da irgendwo in einem Code der im Modul eingebettete Aufzählungstyp verwendet wurde. Und da Sie nicht wissen, mit welcher Version des Serializers das kompilierte Programm funktioniert, kann es nie geändert werden, da es in einer Version der Anwendung funktioniert und in anderen Fällen Ausnahmen auslöst.

11. Sie benötigen keine explizite Methode für den Einstiegspunkt (Sub Main) in WinForms-Anwendungen


Wenn Ihr Projekt Form als Startobjekt verwendet und nicht das „Application Framework“ verwendet (mehr dazu im nächsten Beitrag), generiert VB ein Sub Main , das Ihr Startformular erstellt und an Application.Run übergibt, wodurch Sie entweder die gesamte Datei speichern Um diesen Prozess zu verwalten, entweder eine zusätzliche Methode in Ihrem Form oder sogar die Notwendigkeit, über dieses Problem nachzudenken.

12. Wenn Sie einige veraltete VB-Laufzeitmethoden aufrufen (z. B. FileOpen), wird die aufrufende Methode implizit mit einem Attribut gekennzeichnet, um das Inlining aus Gründen der Korrektheit zu deaktivieren


Kurz gesagt, Methoden zum Arbeiten mit Dateien im VB6-Stil wie FileOpen auf Kontexten, die für die Assembly spezifisch sind, in der sich der Code befindet. Beispielsweise kann Datei 1 ein Protokoll in einem Projekt und eine Konfiguration in einem anderen sein. Um festzustellen, welche Assembly ausgeführt wird, wird Assembly.GetCallingAssembly() aufgerufen. Wenn die JIT Ihre Methode jedoch in den Aufrufer einfügt , wird die VB-Laufzeitmethode aus Sicht des Stapels nicht von Ihrer Methode aufgerufen, sondern vom Aufrufer, der sich möglicherweise in einer anderen Assembly befindet, wodurch Ihr Code auf den internen Status des Aufrufers zugreifen oder diesen verletzen kann Objekt. Dies ist kein Sicherheitsproblem, da Sie bereits verloren haben, wenn der kompromittierende Code in Ihrem Prozess ausgeführt wird. Dies ist eine Frage der Richtigkeit. Wenn Sie diese Methoden verwenden, deaktiviert der Compiler daher das Inlining.

Diese Änderung wurde im letzten Moment im Jahr 2010 vorgenommen, da das x64-JIT beim Inlining / Optimieren des Codes SEHR aggressiv ist. Wir fanden es sehr spät und es war die sicherste Option.

13. Wenn Ihr Typ mit dem DesignerGenerated-Attribut markiert ist und keine expliziten Konstruktordeklarationen enthält, ruft der standardmäßig generierte Compiler InitializeComponent auf, wenn er für diesen Typ definiert ist


In der Zeit vor dem Aufkommen von Partial-Typen führte das VB-Team einen Krieg, um den Code für Boilerplates in WinForms-Projekten zu reduzieren. Aber auch bei Partial dies nützlich, da die generierte Datei den Konstruktor vollständig weglassen kann und der Benutzer sie bei Bedarf manuell in seiner Datei deklarieren oder bei Bedarf nicht deklarieren kann. Ohne dies wäre der Designer gezwungen, einen Konstruktor hinzuzufügen, um nur InitializeComponent aufzurufen. Wenn der Benutzer auch hinzufügt, handelt es sich um Duplikate, oder das Toolkit sollte intelligent genug sein, um den Konstruktor aus der Designer-Datei in die Benutzerdatei zu verschieben und ihn nicht im Designer neu zu generieren, falls er dies bereits getan hat existiert in der Benutzerdatei.

14. Das Fehlen des Modifikators Partial bedeutet NICHT, dass der Typ nicht partiell ist


Technisch gesehen sollte in VB nur eine Klasse als Teilweise markiert werden. Dies ist normalerweise (in GUI-Projekten) eine generierte Datei.

Warum? Dies hält die Benutzerdatei schön und sauber und kann sehr bequem nach dem Generieren oder Hinzufügen von benutzerdefiniertem Code zum generierten Code eingefügt werden. Es wird jedoch empfohlen, dass höchstens eine Klasse keinen Teilmodifikator hat, da sonst eine Warnung ausgegeben wird.

15. In Standardklassen gilt die öffentliche Zugriffsebene für alles außer Feldern und in öffentlichen Strukturen auch für Felder


Ich habe gemischte Gefühle dazu. In C # ist standardmäßig alles private (Prost, Kapselung!). Je nachdem, was Sie häufig deklarieren, gibt es jedoch ein Argument: einen öffentlichen Auftrag oder Details zur Implementierung. Eigenschaften und Ereignisse sind im Allgemeinen für die externe ( public ) Verwendung vorgesehen, und auf Bediener kann nur public zugegriffen werden. Ich verlasse mich jedoch selten auf die Standardzugänglichkeit (mit Ausnahme von Demos wie den Beispielen in diesem Artikel).

16. VB initialisiert die Felder NACH dem Aufruf des Basiskonstruktors, während C # sie initialisiert, BEVOR der Basiskonstruktor aufgerufen wird


Haben Sie gehört, wie "einige" sagen, dass das erste, was im Konstruktor passiert, ein Aufruf des Konstruktors der Basisklasse ist? Nun, das ist zumindest in C # nicht der Fall. In C # werden vor dem expliziten oder impliziten Aufruf von base() die Feldinitialisierer, dann der Konstruktoraufruf und dann Ihr Code ausgeführt. Diese Entscheidung hat Konsequenzen, und ich glaube zu wissen, warum Sprachentwickler in die eine oder andere Richtung gehen könnten. Ich glaube, eine dieser Konsequenzen ist, dass der folgende Code nicht direkt in C # übersetzt werden kann:

 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

In den Tagen, als ich mit Reflection beschäftigt war, habe ich oft solchen Code geschrieben. Und ich erinnere mich vage an einen Kollegen vor Microsoft (Josh), der meinen Code in C # übersetzt hat und sich manchmal über die Notwendigkeit beschwert, alle meine Initialisierer auf den Konstruktor zu portieren. In C # ist es verboten, auf ein Objekt zu verweisen, das vor dem Aufruf von base() wird. Und da die Feldinitialisierer vor dem angegebenen Aufruf ausgeführt werden, können sie auch nicht auf andere Felder oder Mitglieder der Objektinstanz verweisen. Dieses Beispiel funktioniert also auch nur in VB:

 MustInherit Class Base ' OOP OP? Private Cached As Object = DerivedFactory() Protected MustOverride Function DerivedFactory() As Object End Class Class Derived Inherits Base Protected Overrides Function DerivedFactory() As Object Return New Object() End Function End Class 

gist.github.com/AnthonyDGreen/fe5ca89e5a98efee97ffee93aa684e50#file-base-derived-init-vb

Hier haben wir eine Basisklasse, die angeblich viele Funktionen hat, aber ein Schlüsselobjekt für die Verwaltung benötigt, die Arbeit, die vom abgeleiteten Typ bestimmt wird. Es gibt viele Möglichkeiten, eine solche Vorlage zu implementieren, aber ich verwende diese normalerweise, weil:

  • er ist klein;
  • verlangt nicht, dass ich einen Konstruktor deklariere;
  • Ich muss den Initialisierungscode nicht in den Konstruktor einfügen, falls vorhanden.
  • Ermöglicht mir das Zwischenspeichern des erstellten Objekts und erfordert keine abgeleiteten Typen zum Deklarieren und Verwalten des Speichers für das bereitgestellte Objekt, obwohl dies jetzt kein solches Problem mit automatischen Eigenschaften ist.

Außerdem war ich in beiden Situationen: Wenn ein Feld in einem abgeleiteten Typ eine in der Basisklasse deklarierte Methode aufrufen wollte und wenn der MustOverride das vom abgeleiteten Typ implementierte MustOverride Mitglied aufrufen MustOverride . Beide sind in VB gültig und keine in C #, und das ist sinnvoll. Wenn der C # -Feldinitialisierer ein Mitglied der Basisklasse aufrufen könnte, könnte dieses Mitglied von den Feldern abhängen, die im Basiskonstruktor initialisiert wurden (der noch nicht ausgeführt wird), und die Ergebnisse wären mit ziemlicher Sicherheit falsch, und daran führte kein Weg vorbei.

Aber in VB hat der Basiskonstruktor bereits funktioniert, sodass Sie alles tun können! Im umgekehrten Overridable ist alles etwas komplizierter, da das Aufrufen des Overridable vom Initialisierer (oder Konstruktor) der Basisklasse zum Zugriff auf die Felder führen kann, bevor sie "initialisiert" werden. Aber nur Ihre Implementierung weiß, ob dies ein Problem ist. In meinen Skripten passiert das einfach nicht. Sie hängen nicht vom Status der Instanz ab, können jedoch keine Shared Overridable Mitglieder sein, da Sie aus technischen Gründen, die über den Umfang dieses Artikels hinausgehen, kein Shared Overridable Mitglied in einer Sprache haben können. Darüber hinaus ist klar definiert, was mit den Feldern geschieht, bevor benutzerdefinierte Initialisierer gestartet werden. Sie werden wie alle Variablen in VB mit Standardwerten initialisiert. Keine Überraschungen.

Warum also? Eigentlich weiß ich nicht, ob meine Skripte das waren, was das ursprüngliche VB.NET-Team bei der Entwicklung im Sinn hatte. In meinem Fall funktioniert es einfach! , : 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() ' I'm motifying the value of this externally read-only property here. _Status = "Armed" End Sub Function Disarm(code As Integer) As Boolean If code = Me.Code Then ' And here. _Status = "Disarmed" Return True Else Return False End If End Function End Class 

gist.github.com/AnthonyDGreen/57ce7962700c5498894ad417296f9066#file-read-only-auto-property-backing-field-is-writeable-vb

20.


, NonSerialized .

VB (expanded) Custom- 2005 (?) , , , NonSerialized . , , , , «» , « ».

, , , , . , , , , two-way bindable ( , PropertyChanged ), , , , , .

, , CLSA «Expert Business Objects» (Rocky Lhotka) , undo/redo ( , , - , , ), . , . , , , .

Anleitung


21. — , ; ( )


, GoTo . , - . , For For Each ; Using , SyncLock With , , , Finally . If Select Case , Do While , Try — , :

 Module Program Sub Main() Dim retryCount = 0 Try Retry: ' IO call. Catch ex As IO.IOException When retryCount < 3 retryCount += 1 GoTo Retry End Try End Sub End Module 

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 - — , .

 ' You don't have to write this: If PropertyChangedEvent IsNot Nothing Then RaiseEvent PropertyChanged(Me, e) End If ' You don't have to write this: Dim handlers = PropertyChangedEvent If handlers IsNot Nothing Then handlers(Me, e) End If ' You don't have to write this either: PropertyChangedEvent?(Me, e) ' Just write this: RaiseEvent PropertyChanged(Me, e) 

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. , :

  • , .
  • , (, Integer ), .
  • , .

, , , , ( 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 ' No handlers have changed anything. Console.WriteLine("Unchanged.") End If End Sub Event DoSomething(sender As Object, e As MyEventArgs) End Module 

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-vb

Roslyn 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 — ! ( «… … ?» )

Warum? , , , , , #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-vb

F# , IEnumerator , For Each , .

VB , ( well-known name ), . , , Add, . C# , (. async / await ). , C# Roslyn () , .
Minute der Werbung. 15-16 - .NET- DotNext 2019 Piter . , . , . .

Source: https://habr.com/ru/post/de443684/


All Articles