Warum die Ausrede "Das ist aber C#" nicht gilt

    • Allgemein

    Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

      Warum die Ausrede "Das ist aber C#" nicht gilt

      In diesem Artikel werde ich im Detail erklären, warum man auch als VB.Net-Programmierer etwas mit C#-Codes anfangen kann, und worauf man beim Übersetzen achten muss.

      Parallelen zwischen VB und C#
      Zunächst einmal betrachten wir den "Unterbau" der beiden Sprachen - das .Net-Framework. Dieses wurde von Microsoft entwickel, um Softwareentwicklern das Leben zu erleichtern und eine einheitliche Programmumgebung zu schaffen. Demzufolge müssen sich alle Sprachen, die auf dem Framework basieren, an gewisse Vorschriften halten.
      Die wichtigste davon ist die CLR-Kompatibilität. Die CLR (Common Language Runtime) ist die Runtime, die alle .Net-Programme ausführt. Sie garantiert einige Hilfreiche Features wie Plattformunabhängigkeit und Garbagecollection. Eine Sprache ist genau dann CLR-kompatibel, wenn sie ein CLR-kompatibles Typsystem besitzt, also nur Typen (Klassen, Strukturen, usw.) erlaubt, die die CLR verwalten kann. Dies hat den großen Vorteil, dass alle Sprachen, die für die CLR entwickelt wurden, auch untereinander kompatible Typsysteme besitzen, also alle Typen auch allen anderen Sprachen frei zugänglich sind. Ich kann also eine in C# geschriebene Klassenbibliothek ganz normal in eine VB-Anwendung einbinden und alle dort enthaltenen Klassen ganz genauso nutzen, wie die Klassen aus der eigenen Anwendung. Tatsächlich wird man im Studio nicht mal bemerken, dass es keine VB-Lib ist bzw. man kann nicht unterscheiden, ob eine Anwendung in C# oder VB geschrieben wurde.
      Dieses Verhalten liegt an der nächsten Eigenart des Frameworks. Alle Sprachen, die darauf basieren, werden nach einem Klick auf "Kompilieren" in eine Einheitssprache namens "MSIL" (Microsoft Intermediate Language) umgewandelt. Tatsächlich ist dies eigentlich die einzige Sprache, mir der die CLR etwas anfangen kann, alle anderen Sprachen werden also nur CLR-kompatibel, weil sie nach MSIL übersetzt werden. Diesen ILCode kann man noch lesen und teilweise auch wieder in eine Ausgangssprache rückübersetzen (wie z.B. ILSpy das macht). Da dieser ILCode nach dem Übersetzen von VB und C# (und auch allen anderen .Net-Sprachen) aber identisch aussieht, bedeutet das natürlich, dass man z.B. einen aus C# entstanden ILCode auch nach VB umwandeln kann und umgekehrt.
      Ein weiterer Vorteil von .Net-Sprachen ist, dass sie alle die gleiche Klassenbibliothek verwenden, nämlich die das Frameworks. Aufgrund dessen werdet ihr in einem C#-Code die selben Klassen- und Funktionsnamen wiederfinden, wie in einem VB-Code.

      Dann gibt es noch einen weiteren Vorteil speziell für C# und VB. Beides sind objektorientierte, imperative Sprachen, was sie in ihrer logischen Struktur sehr ähnlich macht. Das heißt, C# definiert seine Klassen vom Aufbau her genauso wie VB.
      Zusammen mit den oben genannten Dingen sorgt dies dafür, dass sich die zwei Sprachen bis auf die Syntax fast komplett gleichen, also 1zu1 übersetzt werden können. Dies sieht man daran, dass es einige automatisierte Verfahren zur Übersetzung zwischen den beiden Sprachen in Form von Onlineconvertern gibt. Bei Sprachen wie C++ und Delphi z.B. wäre dies nicht möglich.
      Einen solchern Converter findet ihr beispielsweise hier: codeconverter.sharpdevelop.net/SnippetConverter.aspx

      Unterschiede zwischen VB und C#
      Genug der Theorie, nun werde ich die wichtigsten Unterschiede zwischen VB und C# nennen. Wenn ihr diese Tipps hier befolgt, dann solltet ihr in der Lage sein, 99% aller C#-Codes nach VB zu übersetzen.

      Syntax
      Zunächst einmal wäre da natürlich die Syntax. Wären VB.Net sich am alten VB6 und/oder VBA orientiert, übernimmt C# viele Elemente aus C/C++.
      Ich kann hier nicht die komplette Syntax von C# erklären, aber ich werde einige signifikante Punkte besprechen.
      1. Codezeilen in C# enden immer mit einem Semikolon. Dadurch wird es möglich, theoretisch beliebig viel Anweisungen in eine Zeile zu schreiben, da der Zeilenumbruch in C# im Gegensatz zu VB keine Bedeutung hat (macht euch darüber weniger sorgen, kein anständiger C#-Programmierer tut das).
      2. In C# gibt es keinen syntaktischen Unterschied zwischen einer Methode und einer Funktion. Eine Methode ist einfach eine Funktion, die void (zu Deutsch "nichts") zurückgibt.

        VB.NET-Quellcode

        1. Public Sub Test1()
        2. Public Function Test2() As Integer
        wird zu

        C-Quellcode

        1. public void Test1()
        2. public int Test2()


      3. Alle Anweisungsblöcke (Klassen, Funktionen, Schleifen, If-Abfragen, usw...) werden mit geschweiften Klammern eingeschlossen, anstatt wie bei VB mit einer End-Anweisung beendet.

        VB.NET-Quellcode

        1. Public Function Test() As Integer
        2. End Function
        wird zu

        C-Quellcode

        1. public int Test()
        2. {
        3. }


        Es gibt allerdings eine Ausnahme bei If-Abfragen und Schleifen: wenn auf solche kein Klammernpaar folgt, so ist darauf folgene Anweisung die einzige, die sich innerhalb diese Blockes befindet. Ein ähnliches Konstrukt gibt es bei VB auch mit If.

        normale Abfrage in VB:

        VB.NET-Quellcode

        1. If True Then
        2. MessageBox.Show("Test")
        3. End If

        kurze Abfrage in VB:

        VB.NET-Quellcode

        1. If True Then MessageBox.Show("Test")

        normale Abfrage in C#:

        C-Quellcode

        1. if (true)
        2. {
        3. MessageBox.Show("Test");
        4. }

        kurze Abfrage in C#:

        C-Quellcode

        1. if (true)
        2. MessageBox.Show("Test");

      4. C# hat Klammerzwang hinter Parameterlosen Methoden/Funktionen.

        VB.NET-Quellcode

        1. Public Sub Test()
        2. End Sub
        3. Public Sub Main()
        4. Test
        5. End Sub
        wird zu

        C-Quellcode

        1. public void Test()
        2. {
        3. }
        4. Public void main()
        5. {
        6. Test(); //hier die Klammern beachten
        7. }
        Wenn ihr diese Klammern weglasst, dann werdet ihr einen saftigen Fehler geworfen bekommen, da die Methode ohne Klammern in C# etwas ganz anderes bedeutet (siehe "Events").

      5. C# ist case-sensitive, das bedeutet, dass test und Test etwas verschiedenes ist.
        Dadurch sind z.B. solche Dinge möglich:

        C-Quellcode

        1. int someValue;
        2. public int SomeValue
        3. {
        4. get
        5. {
        6. return someValue;
        7. }
        8. set
        9. {
        10. someValue = value;
        11. }
        12. }

        In VB würde diese Namensgebung Probleme machen. Ihr müsst dann gegebenenfalls den Namen der Variable ändern:

        VB.NET-Quellcode

        1. Private _someValue As Integer
        2. Public Property SomeValue As Integer
        3. Get
        4. Return _someValue
        5. End Get
        6. Set(value As Integer)
        7. _someValue = value
        8. End Set
        9. End Property


      Schlüsselwörter
      VB und C# unterstützen die gleichen Modifizierer, jedoch heißen sie unterschiedlich. Es folgt nun eine List im Stil <VB-Name> - <C#-Name>.
      • Friend - internal
      • Shared - static
      • Overrides - override
      • Overrideable - virtual
      • MustOverride - abstract
      • MustInherit - abstract
      • NotInheritable - sealed

      Events
      Einen sehr großen Unterschied weisen VB und C# im Umgang mit Events auf. Ich werde nun erst einmal erklären, was ein Event überhaupt für den Computer darstellt.
      Ein Event ist zunächst ein einfacher Delegat. Ein Delegat kann man sich grob als eine Ansammlung von Zeigern auf Methoden/Funktionen vorstellen. Wenn man einen Delegaten aufruft, dann ruft man im Prinzip nur alle Methoden/Funktionen auf, auf die der Delegat zeigt. Delegaten sind somit .Nets Functionpointer (bekannt aus C++). Wenn wir ein Event abonnieren, dann fügen wir dieser Liste eigentlich nur den Zeiger auf eine Methode/Funktion hinzu. VB verschleiert diesen Prozess, um es einsteigerfreundlicher zu machen, indem die Schlüsselwörter AddHandler und RemoveHandler eingefürt wurden.
      Events sind meinst von Delegatentyp Eventhalndler können aber theoretisch von jedem Delegatentyp sein.

      Angenommen wir haben diese Eventdeklaration:

      VB.NET-Quellcode

      1. Public Event TestEvent As EventHandler

      C-Quellcode

      1. public event EventHandler TestEvent

      Dann würden wir mir diesem Code in VB das Event abonnieren und deabonnieren:

      VB.NET-Quellcode

      1. AddHandler TestEvent, AddressOf TestMethod
      2. RemoveHandler TestEvent, AddressOf TestMethod
      Dabei holen wir uns den Zeiger zu der Methode mit dem AddressOf-Schlüsselwort.
      C# kennt kein solches Schlüsselwort. Stattdessen können wir gleich die Methode selbst dem Delegaten hinzufügen. Dies geschieht mit den Operatoren + und -.

      C-Quellcode

      1. TestEvent += TestMethod;
      2. TestEvent -= TestMethod;
      Beachtet hier die Schreibweise ohne Klammern. Die Klammern stehen in C# für einen Aufruf, werden sie weggelassen so wird der Zeiger angesprochen.

      Beachtet auch, dass man in C# vor dem Aufrufen eines Events dieses immer auf null prüfen muss, falls noch keine Zeiger hinzugefügt wurden. VB übernimmt dies für einen.

      VB.NET-Quellcode

      1. RaiseEvent TestEvent(Me, EventArgs.Empty)
      wird zu

      C-Quellcode

      1. if (TestEvent != null)
      2. TestEvent(this, EventArgs.Empty);


      Lambda
      VB und C# unterstützen beide Lambda, also sogenannte anonyme Methoden. Die Syntax unterscheidet sich aber so deutlich, dass ich es hier ebenfalls bespreche.

      VB verwendet zum deklarieren solcher anonymer Methoden die selben Schlüsselwörter, wie bei den nichtanonymen:

      VB.NET-Quellcode

      1. list.Sort(Function(item1, item2) item1.Value.CompareTo(item2.Value))
      oder mehrzeilig

      VB.NET-Quellcode

      1. list.Sort(Function(item1, item2)
      2. Return item1.Value.CompareTo(item2.Value)
      3. End Function)

      In C# schreibt man einfach nur die Argumente gefolgt von einem => an diese Stelle:

      C-Quellcode

      1. list.Sort((item1, item2) => item1.Value.CompareTo(item2.Value));
      oder mehrzeilig

      C-Quellcode

      1. list.Sort((item1, item2) =>
      2. {
      3. return item1.Value.CompareTo(item2.Value);
      4. });
      Achtung: wenn es nur ein Argument gibt, dann können in C# die Argumentklammern auch weggelassen werden.

      Konstruktoren
      In VB deklariert man einen Konstruktor, indem man eine Methode mit dem Namen New erstellt. Dies ist für Einsteiger vermutlich leichter, da Objekte ebenfalls mit New erzeugt werden und man somit sieht, dass dann diese Methode aufgerufen wird.

      VB.NET-Quellcode

      1. Public Class Test
      2. Public Sub New()
      3. End Sub
      4. End Class

      Da C# sich aber nach C richtet, ist der Konstruktor dort keine normale Methode, da sie keinen Rückgabetyp besitzt (auch nicht "void"). Außerdem trägt sie den Namen der Klasse.

      C-Quellcode

      1. public class Test
      2. {
      3. public Test()
      4. {
      5. }
      6. }

      Außerdem gibt es in C# noch einen sogenannten Destruktor, der in VB fehlt und aufgerufen wird, wenn das Objekt durch den Garbagecollector zerstört wird. Dessen Syntax ist identisch zu der des Konstruktors, mit der Ausnahme, dass vor den Namen noch ein ~ gesetzt wird.

      C-Quellcode

      1. public class Test
      2. {
      3. public ~Test()
      4. {
      5. }
      6. }

      Eine ähnliche Funktionalität kann man in VB durch die Methode Finalize erreichen, die laut MS der offizielle Ersatz für den Destruktor ist.

      Module
      In VB gibt es Module, welche es erlauben, Methoden und Funktionen zu deklarieren, die man unabhängig von einer Klasse verwenden kann. Diese sind aber ein Überbleibsel aus VB6, weshalb sie keinen Einzug in C# gefunden haben. Dort verwendet man für diese Zwecke statische Klassen.

      VB.NET-Quellcode

      1. Public Module Test
      2. End Module
      wird zu

      C-Quellcode

      1. public static class Test
      2. {
      3. }


      Properties
      Obwohl beide Sprachen Properties (Eigenschaften) unterstützen, verhalten sie sich hier doch etwas anders.
      Währen in VB Properties auch Parameter besitzen können, ist dies in C# nicht ohne weiteres möglich. Dort muss man gegebenenfalls auf einen Getter und einen Setter zurückgreifen.

      VB.NET-Quellcode

      1. Public Property Test(s As String) As String
      2. Get
      3. End Get
      4. Set
      5. End Set
      6. End Property
      wird zu

      C-Quellcode

      1. public string GetTest(string s)
      2. { }
      3. public void SetTest(string s, string value)
      4. { }

      Eine Ausnahme bildet der sogenannte Indexer, mit diesem sind auch in C# Argumente bei Properties möglich. Er entspricht der Default-Property aus VB und hat immer den Namen this.

      VB.NET-Quellcode

      1. Public Default Property Test(s As String) As String
      2. Get
      3. End Get
      4. Set
      5. End Set
      6. End Property
      wird zu

      C-Quellcode

      1. public string this[string s]
      2. {
      3. get { }
      4. set { }
      5. }

      Aus C# würde er so aufgerufen werden:

      C-Quellcode

      1. TestObject["TestString"]

      In VB ist er zusätzlich noch unter dem Namen Item wie eine normale Property verfügbar:

      VB.NET-Quellcode

      1. 'entweder
      2. TestObject("TestString")
      3. 'oder
      4. TestObject.Item("TestString")


      Unsafe
      C# besitzt einen sogenannten Unsafe-Block. Dieser erlaubt Zugriffe auf Pointer und fehlt in VB vollkommen.
      Möchte man einen sochen nach VB.Net übersetzen, so gibt es leider keine einheitliche Vorgehensweise, hier ist der Einfallsreichtum des Programmierers gefragt. Hilfreich könnte Marshalling sein, vielleicht gibt es aber auch eine bessere Möglichkeit.
      Da das Thema zu komplex für diesen Artikel wäre, werde ich es komplett außen vor lassen.


      Wenn ihr noch weitere Beispiele/Vergleiche braucht, hier findet ihr eine große Liste (danke fichz).

      Wenn ich noch was vergessen habe oder ich mich ungenau/unverständlich ausgedrückt haben sollte, so bitte ich darum mir dies mitzuteilen.

      Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von „Artentus“ ()

      Sehr gut geschrieben wie ich finde :)
      Ich hatte mal eine Seite gefunden wo auch einzelnen Codeblöcke in VB und C# nebeneinander geschrieben stehen:
      harding.edu/fmccown/vbnet_csharp_comparison.html

      Eventuell kannst diese ja in den Startpost editieren,

      lg
      ScheduleLib 0.0.1.0
      Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
      Punkt 1: Fail, war neben der Sache, sorry, C# ist nat. .NET und da gibt es ja normale System.Strings...
      Punkt 2: So gesehen ist If auch veraltet, hätte man nat. auch rausnehmen müssen, aber gut, ohne das geht nat. nix...
      #define for for(int z=0;z<2;++z)for // Have fun!
      Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

      Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Trade“ ()

      @Trade
      Ich glaube du verwechselst gerade C# mit C++. Bei einem String besteht zwischen VB und C# überhaupt kein Unterschied, sonst wäre die CLR-Kompatibilität ja nicht gegeben (siehe oben).

      Und das mit dem If ist kein Argument, denn If ist eine imperative Kontrollstruktur und hat nichts damit zu tun, ob eine Sprache jetzt OOP ist oder nicht. Ohne ein derartiges Konstrukt könnte eine Sprache niemals Turingvollständigkeit erreichen.
      Finde ich gut. Jedoch noch eine kleine Anmerkung. Ich verwende zwar kein Visual Basic, jedoch ist laut Microsoft die Finalize Methode nicht nur als ähnliches Konstrukt zum Destruktor zu sehen sondern vielmehr als quasi der Ersatz dafür. Wobei jedoch Dispose nicht annähernd mit einem Destruktor verglichen werden kann. Vielmehr ist es so, dass die überschriebene Finalize-Methode Dispose aufrufen soll(jedoch nicht Dispose() sondern Dispose(Boolean). -> Siehe korrekte Implementation von IDisposable. Ansonsten alles sehr schön gemacht. Werde wohl noch oft darauf verweisen :)


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Ich würde vielleicht für den Aufruf von Events noch ?. von C# 6 hinzufügen.
      Eigentlich müsste der Call so aussehen:

      C#-Quellcode

      1. EventHandler testEvent = TestEvent;
      2. if (testEvent != null)
      3. testEvent(this, EventArgs.Empty);

      Bei deiner Schreibweise könnte eine NullReferenceException auftreten, wenn das if überprüft ob das Event null ist, und anschließend die letzte Methode "unsubscribed", dann ist das event plötzlich doch wieder null. Durch das kopieren in die lokale Variable wird das verhindert.

      In C#6 sieht der ganze Code jedoch so aus:

      C#-Quellcode

      1. TestEvent?.Invoke(...);

      Damit wird TestEvent nur einmal "ausgelesen", d.h. es wird genau in diesem Zustand Invoke aufgerufen, zu welchem Zeitpunkt die null Überprüfung stattfand. Ist also einfacher kürzer und schöner. Außerdem passieren dann nicht solche Fehler, wie sie bei deiner Schreibweise noch passieren könnten.
      Ich wollte auch mal ne total überflüssige Signatur:
      ---Leer---

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „jvbsl“ ()

      Etwas was eventuell nicht gerade zum Thema passt, aber denoch ein sehr wichtiges Detail bezüglich beider Sprachen ist, sind die Überläufe.

      Während in C# so etwas problemlos möglich ist in der Grundeinstellung

      C#-Quellcode

      1. uint a = 50;
      2. uint b = 100;
      3. uint c = uint.MaxValue - 50;
      4. uint d = unchecked(a - b); // d = 4294967246
      5. uint e = c + b; // e = 49
      6. d = checked(a - b); // Error
      7. e = checked(c + b); // Error
      8. //Ist einstellbar unter
      9. //Project >> Properties >> Build >> Advanced >> checks for arimethics overflow/underflow
      10. d = a - b; // Error
      11. e = c + b; // Error



      wird sowas in Vb.Net in der Grundeinstellung mit einer Errormeldung Arithmetic operation resulted in an overflow während der Laufzeit ausgegeben.

      VB.NET-Quellcode

      1. Dim a As UInteger = 50
      2. Dim b As UInteger = 100
      3. Dim c As UInteger = UInteger.MaxValue - 50
      4. Dim d As UInteger = a - b 'Error
      5. Dim e As UInteger = c + b 'Error
      6. 'Ist einstellbar unter
      7. 'Project >> Properties >> Advanced Compile Options >> Remove integer overflow checks
      8. d = a - b 'd = 4294967246
      9. e = c + b '49


      In C# kann man sich das zum Vorteil nutzen. Wenn nicht erwünscht muss es entsprechend wie oben dargestellt beachtet werden. Vb.Net ist da sehr konsequenz und verlangt diesbezüglich eine klare vorgehendsweise. Es gibt aber auch hier eine Möglichkeit über eine selber gemachte (un)checked siehe hier


      Edit: Danke jvbsl. Ich wusste gar nicht das es in VB.Net auch einstellbar ist.

      Freundliche Grüsse

      exc-jdbi

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

      Das Ding it Projektweit kannst du einstellen, ob overflows eine Exception throwen sollen(Projektweit), das kann man bei C# und VB.Net machen.

      Jedoch kann man es bei C# genauer kontrollieren, da es dort die unchecked/checked keywords gibt
      Ich wollte auch mal ne total überflüssige Signatur:
      ---Leer---
      Ich weiß um den ?.-Operator in C# 6, allerdings gehe ich nicht bei jeder neuen Sprachversion alle meine Posts durch und passe sie entsprechend an. ;) Die alte Vorgehensweise wird deswegen ja nicht falsch, ReSharper merkt sowas dann sogar als Korrekturvorschlag an.

      Über das Problem im null-Check hab ich ehrlich gesagt noch gar nicht nachgedacht. Ist aber schon ein enormer Grenzfall, da muss man irgend was sehr suspektes mit Threading anstellen, um in so eine Situation zu kommen. Sowas ist mir persönlich nicht nicht untergekommen.

      Artentus schrieb:

      nicht untergekommen
      Wenn man mitb solchen Situationen ernsthaft umgehen muss, sollte man die Firma wechseln.
      Das grenzt doch an Sabotage. :/
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Der Destruktor darf keinen Zugriffsmodifizierer haben.

      C# Spezifikation schrieb:


      Destructors cannot have parameters, they cannot have accessibility modifiers, and they cannot be invoked explicitly

      Sieht also so aus:

      C#-Quellcode

      1. public class Foo
      2. {
      3. ~Foo()
      4. {
      5. //Inhalt
      6. }
      7. }


      Eine ähnliche Funktionalität kann man in VB durch die Methode Finalize erreichen
      Nicht nur eine ähnliche Funktion, sondern exakt die selbe. Der Destruktor in C# ist auch nur die überschriebene Finalize-Methode.
      Es gibt nur einen kleinen Unterschied: Es wird automatisch ein Try-Finally drumherum gepackt. Der Destruktor ist das Äquivalent zu dem hier:

      C#-Quellcode

      1. protected override void Finalize()
      2. {
      3. try
      4. {
      5. //Inhalt
      6. }
      7. finally
      8. {
      9. base.Finalize();
      10. }
      11. }
      Außer, dass C# das manuelle Überschreiben von Finalize nicht erlaubt, weil es ja den Destruktor gibt ("Überschreiben Sie nicht object.Finalize, sondern stellen Sie einen Destruktor bereit.") und den Aufruf an base.Finalize nicht erlaubt, weil das automatisch gemacht wird ("Rufen Sie die Finalize-Methode Ihrer Basisklasse nicht direkt auf, da sie automatisch vom Destruktor aufgerufen wird.").
      In VB wird das nicht automatisch eingefügt.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils