Knifflige Übersetzung C# nach VB.NET

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von us4711.

    Knifflige Übersetzung C# nach VB.NET

    Ich bin mit meinen C#-Kenntnissen mit folgendem Snippet überfragt:

    C#-Quellcode

    1. foreach (
    2. IGrouping<string, FileSystemInfo> grouping in
    3. from p in paths group p by Path.GetDirectoryName(p.FullName))
    4. {
    5. FilesOrFolders(Path.GetDirectoryName(grouping.First<FileSystemInfo>().FullName),
    6. (from fsi in grouping select fsi.Name).ToList<string>());
    7. }

    Auch die gängigen Übersetzungsprogramme machen daraus nix Verwertbares.
    Vielleicht gibt es ja hier eine Koryphäe, die das hinkriegt.
    Übrigens:
    Das Originaprojekt in C# kompiliert und läuft einwandfrei.
    Umgeschrieben in Fluent-Linq müsste der Code folgendermaßen aussehen:

    C#-Quellcode

    1. foreach (var grouping in paths.GroupBy(p => Path.GetDirectoryName(p.FullName)))
    2. {
    3. FilesOrFolders(Path.GetDirectoryName(grouping.First<FileSystemInfo>().FullName), grouping.Select(fsi => fsi.Name).ToList<string>());
    4. }​

    Das lässt sich dann auch gut übersetzen:

    VB.NET-Quellcode

    1. ​For Each grouping In paths.GroupBy(Function(p) Path.GetDirectoryName(p.FullName))
    2. FilesOrFolders(Path.GetDirectoryName(grouping.First(Of FileSystemInfo)().FullName), grouping.Select(Function(fsi) fsi.Name).ToList(Of String)())
    3. Next
    @ErfinderDesRades
    Eine kleine Änderung war nötig:

    VB.NET-Quellcode

    1. For Each grouping In paths.GroupBy(Function(p) Path.GetDirectoryName(p.FullName))
    2. FilesOrFolders(Path.GetDirectoryName(grouping.First.FullName), grouping.Select(Function(fsi) fsi.Name).ToList)
    3. Next

    Path.GetDirectoryName(grouping.First(Of FileSystemInfo)().FullName) => Path.GetDirectoryName(grouping.First.FullName '.First(Of T) gibt's in VB wohl nicht.
    .ToList(Of String) => .ToList '. List(Of T) gibt's in VB wohl nicht.

    @Fakiz
    Korrekt:
    Dim paths as IEnumerable(Of FileSystemInfo)



    @us4711
    gibt's in VB wohl nicht

    Dem habe ich, weil ich so pedantisch bin, was hinzuzufügen.

    First und ToList sind Extension-Methoden für den IEnumerable(Of T)-Typ.
    Also Methoden, die so aussehen:

    VB.NET-Quellcode

    1. <Extension()>
    2. Sub Foo(Target As Bar)
    3. End Sub

    können auf diese beiden Arten aufgerufen werden:

    VB.NET-Quellcode

    1. Dim B As Bar = ...
    2. Foo(B) 'Die "normale" Art
    3. B.Foo() 'Als Extension

    Das hat eine ganze Menge Vorteile. Du kannst mal die statischen Funktionen in System.Linq.Enumerable durchschauen. Viele davon sind Extensions.

    Die ToList-Funktion sieht so aus:

    VB.NET-Quellcode

    1. <Extension()>
    2. Public Function ToList(Of TSource)(source As IEnumerable(Of TSource)) As List(Of TSource)
    3. '...
    4. End Function

    Also nimmt eine beliebige Auflistung von TSource-Objekten entgegen, wirft jedes Objekt in eine List(Of TSource) und gibt die List zurück.
    Diese Funktion kann man jetzt wieder auf zwei Arten aufrufen:

    VB.NET-Quellcode

    1. Dim Numbers1 As IEnumerable(Of Integer) = Enumerable.Range(1, 100) 'Alle Integer von 1 bis 100.
    2. Dim Numbers2 As List(Of Integer) = ToList(Numbers1) '"normal"
    3. Dim Numbers3 As List(Of Integer) = Numbers1.ToList() 'Extension

    Hier hilft der Compiler aber ein bisschen nach. Voll ausgeschrieben müsste es heißen:

    VB.NET-Quellcode

    1. Dim Numbers1 As IEnumerable(Of Integer) = Enumerable.Range(1, 100) 'Alle Integer von 1 bis 100.
    2. Dim Numbers2 As List(Of Integer) = ToList(Of Integer)(Numbers1) '"normal"
    3. Dim Numbers3 As List(Of Integer) = Numbers1.ToList() 'Extension

    Der Compiler kann hier auch so feststellen, welche Typen gemeint sind, deshalb muss man es nicht hinschreiben. Aber es gibt auch Situationen, in denen er es nicht mehr eindeutig feststellen kann. Dann muss man es explizit hinschreiben.
    Beachte aber, dass in Zeile 3 ToList(Of Integer), in Zeile 4 aber nur ToList ohne (Of ...) steht. Wie kommt das zustande?

    Nun, der generischen Typenparameter der ToList-Funktion, nämlich TSource, gibt bei einem "normalen" Aufruf an, wie die Typen der formalen Parameter aussehen müssen. Bei Zeile 3 schreibt man einen ganz normalen Funktionsaufruf hin. Und weil man für TSource Integer gewählt hat, muss der Typ des source-Parameters IEnumerable(Of Integer) sein. Macht soweit Sinn.

    Überträgt man das auf Zeile 4 und man müsste Numbers.ToList(Of Integer)(...) hinschreiben, würde das sehr ungewöhnlich aussehen. Denn wenn die ToList-Funktion tatsächlich als Instanz-Funktion im IEnumerable(Of T]-Typ deklariert wäre, und keine Extension wäre, dann...

    VB.NET-Quellcode

    1. Public Interface IEnumerable(Of T)
    2. '... wäre es unsinnig, der Funktion hier nochmal einen generischen Typenparameter TSource zu geben, denn der Typ der Elemente in der Liste ergibt sich ja aus der Tatsache, dass die Funktion im IEnumerable(Of T)-Typ deklariert ist, und der hat ja schon T.
    3. Public Function ToList(Of TSource)() As List(Of TSource)
    4. '...
    5. End Function
    6. 'Stattdessen würde die Funktion so aussehen. Also komplett ohne generische Typenparameter.
    7. Public Function ToList() As List(Of T)
    8. '...
    9. End Function
    10. '(Sonstiges Zeugs)
    11. End Interface
    (wir ignorieren hier mal, dass man in Interfaces keine Methoden mit Körper haben kann.)

    Jetzt sollte es logisch sein, dass man bei einem Aufruf an die Instanz-Funktion (wenn es eine wäre) nur ToList ohne (Of ...) verwendet.
    Und der Sinn von Extensions ist ja, dass sie aussehen, als wären sie Instanz-Funktionen. Man soll von außen also nicht den Unterschied sehen. Da wäre es jetzt doof, wenn man im Falle einer Extension TSource angeben müsste und im Falle einer Instanz-Funktion nicht.
    Deswegen gibt es diese Regel, dass alle generischen Typenparameter einer Extension, die sich durch den Typ des Objektes ergeben, an dem diese Extension aufgerufen wird, ausgeblendet werden, denn dann kann man tatsächlich keinen Unterschied mehr erkennen. Hier wird die Reihenfolge also umgedreht. Nicht der generische Typenparameter gibt die Typen der formalen Parameter vor, sondern der erste formale Parameter gibt die betroffenen generischen Typenparameter vor.
    Im Falle von ToList und First gibt es nur einen einzigen generischen Typenparameter. Und der ergibt sich eben aus dem Objekt, an dem die Funktion aufgerufen wird. Deswegen wird er ausgeblendet. Und deshalb sieht es so aus, als würde es ToList(Of T) nicht geben, sondern nur ToList.

    In C# wird das allerdings nicht so gehandhabt. Da muss man weiterhin alle generischen Typenparameter hinschreiben (sofern der Compiler nicht selbst draufkommt natürlich). Dadurch hat man diese Inkonsistenz zwischen Instanz-Funktionen und Extensions und es hat obendrein die interessante Implikation, dass man es auch falsch machen kann:

    C#-Quellcode

    1. IEnumerable<int> Numbers1 = Enumerable.Range(0, 100);
    2. IEnumerable<int> Numbers2 = Numbers1.ToList<string>();

    Für TSource wird hier string angegeben, also müsste das Objekt, an dem die ToList-Funktion aufgerufen wird (also Numbers1), vom Typ IEnumerable<string> sein, es ist aber IEnumerable<int>. Der Compiler beschwert sich darüber:

    Instanzenargument: Konvertierung von "IEnumerable<int>" in "IEnumerable<string>" ist nicht möglich.
    "IEnumerable<int>" enthält keine Definition für "ToList", und die Überladung der optimalen Erweiterungsmethode "System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource>)" weist einige ungültige Argumente auf.

    Mir persönlich gefällt die Herangehensweise von VB besser, aber ich kann mir Argumente für beide Seiten vorstellen. In C# hätte es wohl im ersten Moment nicht für Verwirrung gesorgt.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    in c# geht das kein deut anners als in vb.net.
    Bei Extensions gibt man normal keinen TypParameter an - wie gesagt: nur in seltenen uneindeutigen Fällen.
    Also bereits der c#- Code ist schon ziemlich unüblich, und ich weiß nichtmal, ob der kompilieren täte.




    anneres Thema: Man kann auch mal gucken, was der Code machen soll: Wie's aussieht, bekommt er viele FilesystemINfos, und soll die Gruppieren nach den Directories.
    Das könnte man etwas klarer formulieren:

    VB.NET-Quellcode

    1. For Each grp In fsis.GroupBy(Function(fsi) Path.GetDirectoryName(fsi.FullName)) ' keySelector: gruppiert die fsis nach DirectoryNames
    2. FilesOrFolders(grp.Key, grp.Select(Function(fsi) fsi.Name).ToList)
    3. Next
    probier ma aus.
    (ausserdem habich paths jetzt fsis genannt - k.A., ob das besser ist.