
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 TextSyntax 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()
gist.github.com/AnthonyDGreen/39634fd98a0cacc093719ab62d7ab1e6#file-partial-re-implementation-vbIn 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)
gist.github.com/AnthonyDGreen/863cfd1e7536fe8bda7cd145795eaf9f#file-shadows-example-vbDie 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))
gist.github.com/AnthonyDGreen/ce12ac986219eb51d6c85fa02c339a2f#file-protected-in-generics-vb8. 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
gist.github.com/AnthonyDGreen/fd1e5e3a58aee862a5082e1d2b078084#file-root-namespace-vbDaher 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-vbIn 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
gist.github.com/AnthonyDGreen/fe5ca89e5a98efee97ffee93aa684e50#file-base-derived-init-vbHier 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()
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 ( , , - , , ), . , . , , , .
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:
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 — ! (
«… … ?» )
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-vbF# ,
IEnumerator
,
For Each
, .
VB , (
well-known name ), . , , Add, . C# , (.
async
/
await
). , C# Roslyn () , .
Minute der Werbung. 15-16 - .NET- DotNext 2019 Piter . , . , . .