none
Überladen von Deconstruct RRS feed

  • Frage

  • Ich schaue mir gerade die Neuerungen in C# an und habe eine Frage zum deconstruct:

    using System; namespace deconstruct { class Program { class Wert { public int W1 { get; } public int W2 { get; } public float F { get; } public Wert(int w1, int w2, float f) { W1 = w1; W2 = w2; F = f; } public void Deconstruct(out int w1, out int w2) { w1 = W1; w2 = W2; } public void Deconstruct(out int w1, out float f) { w1 = W1; f = F; } } static void Main(string[] args) { Wert wert = new Wert(1, 2, 3f);

    (int A_w1, int A_w2) = wert; // CS0121 // Der Aufruf unterscheidet nicht eindeutig zwischen den folgenden Methoden oder Eigenschaften: // "Program.Wert.Deconstruct(out int, out int)" und "Program.Wert.Deconstruct(out int, out float)" (int B_w1, float B_f) = wert; // CS0121 // Der Aufruf unterscheidet nicht eindeutig zwischen den folgenden Methoden oder Eigenschaften: // "Program.Wert.Deconstruct(out int, out int)" und "Program.Wert.Deconstruct(out int, out float)" // Fehlerfrei ist allerdings der direkte Aufruf: wert.Deconstruct(out A_w1, out A_w2); wert.Deconstruct(out B_w1, out B_f); } } }


    Das ich Deconstruct mit unterschiedlicher Parameteranzahl überladen kann ist mit bekannt. Und rein funktional kann ich auch mit gleicher Anzahl aber unterschiedlichen Typen überladen.

    Leider klappt dann allerdings die Zuweisung über Tupel nicht (siehe CS0121):

    (int B_w1, float B_f) = wert;

    Der direkte Aufruf funktioniert allerdings:

    wert.Deconstruct(out B_w1, out B_f);


    Ist das logisch, dass das nicht geht? Gibt es dafür eine Erklärung*?

    Gruß

    Ralf

    * Außer, das man in C# nicht durch unterschiedliche Rückgabewerte überladen kann. Nur Deconstruct ist als void definiert und hat auch keinen return-Befehl.

    PS: Man muss übrigens immer noch System.ValueTuple laden. Eigentlich sollte das doch zu c# 7 dazugehören, oder?

    Edit:

    Noch eine kleine Ergänzung:

    (int a, int b) = wert;

    ist etwas anderes als

    (int a, int b) ci = wert;

    Der erste, anonyme Tupel-Fall löst ein deconstruct-Aufruf aus.
    Der zweite, benannte Tupel-Fall löst eine Zuweisung aus und benötig somit einen operator in der Klassendefinition:

    static public implicit operator (int x, int y) (Wert w)
    {
      return (w.W1, w.W2);
    }
    static public implicit operator (int x, float y) (Wert w)
    {
      return (w.W1, w.F);
    }

    Dieser kann allerdings sehr wohl zwischen den verschiedenen Typen unterscheiden und somit funktioniert auch:

    (int a, int b) ci = wert;
    (int a, float b) cf = wert;

    ci hat anschließend die Werte (1, 2) und
    cf die Werte (1,3).



    Mittwoch, 8. März 2017 18:51

Alle Antworten

  • Hallo Ralf,

    Man muss übrigens immer noch System.ValueTuple laden. Eigentlich sollte das doch zu c# 7 dazugehören, oder?

    System.ValueTuple ist in .NET Framework 4.6.2 nicht enthalten. Probiere es mit der Installation des entsprechenden Nuget-Pakets. Sieh Dir diesen Thread an:
    Tuple syntax in VS 2017

    Gruß,
    Dimitar


    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Donnerstag, 9. März 2017 15:12
    Administrator
  • Danke für die Info. Installiert hatte ich das Nuget-Paket ja schon. Hatte mich nur gewundert, das es noch nötig war. Aber die Versionsbegründung ist ja nachvollbar.

    Bleibt die Frage, ob es richtig ist, das der Overload von Deconstruct zwar definiert, aber nicht genutzt werden kann.

    Donnerstag, 9. März 2017 17:39
  • Hallo Ralf,

    beim Überladen von Operatoren - in deinem Beispiel implizite, die das Objekt in ein Tuple<T1,T2> verwandeln - funktioniert dein Vorgehen, da es dort auf den verwendeten Typ ankommt und dieser unterscheidet sich nun mal.

    Zu Deconstruct: Ich bin noch nicht all zu gut belesen was die C# 7 Features betrifft, möchte allerdings kurz sagen, dass die (...)=obj Syntax nur Syntatic sugar für einen ganz normaler Aufruf der Deconstruct-Methode ist. Das kann man auch an dem erzeugten IL Code nach einem erneuten dekompilieren sehen.

    Warum das nun trotzdem nicht funktioniert weiß ich gerade auch nicht. Ich installiere erstmal das Release von VS2017 und teste dann mal weiter. Bisherige Vermutung: Bei der Bestimmung der passenden Methode wird nur auf die Anzahl der Parameter geachtet und die Typen ignoriert.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    Freitag, 10. März 2017 01:58
    Moderator
  • Hallo Tom,

    so richtig mag mir der Sinn von Deconstruct nicht in den Kopf kommen, da ich ja immer eine Eigenschaft

    public (...) Name
    {
      get {return (...);}
    }

    definieren kann, die dann ja im Prinzip nichts anderes macht als die entsprechend gleiche Deconstruct-Methode. Nur mit dem Vorteil, im Code klarer zu zeigen, was eigentlich gemacht werden soll:

    // implizit
    (int id, string name) = maschine;
    (float geschwindigkteit, float beschleunigung) = maschine;
    (string utm, float höhe) = maschine;
    
    
    // via Eigenschaft
    (int id, string name) = maschine.IdName;
    (float geschwindigkteit, float beschleunigung) = maschine.Motorwerte;
    (string utm, float höhe) = Maschine.Position;

    Wo ist der besondere Vorteil von Deconstruct, außer, dass ich beim Aufruf keinen Namen angeben muss (was im Überladungsfall m. E. nicht wirklich ein Vorteil ist, da der Code dadurch nicht besser lesbar wird).

    Danke und Gruß

    Ralf


    Freitag, 10. März 2017 07:42
  • Hallo Ralf,

    die ganze Tupel-Sache ist sehr mit Vorsicht zu genießen. Du hast recht, dass es relativ schnell unübersichtlich werden kann.
    Wenn du jemals mit einer Sprache wie F#, Haskell etc. gearbeitet hast, dann weißt du dass es noch vollkommen andere Konzepte als OOP usw. gibt. In logischen Kontexten finde ich diese manchmal deutlich besser (da kürzer), da gehören auch die Tupel dazu. Schließlich ist (x,y) nun mal schon irgendwie ein Punkt/Point-Objekt.

    Das Standardbeispiel mit dem Deconstruct einer Person-Instanz wo man Name und FamilyName raus bekommt finde ich regelrecht dämlich. Als ob das alles wäre was das Objekt ausmacht. Da würde ich unbedingt weiterhin die Eigenschaften einzeln abfragen. 

    Aus den VS 2017 RC Zeiten kenne ich aber ein für mich schönes Beispiel. Ich habe eine Erweiterungsmethode für KeyValuePait<TKey, TValue> geschrieben, die mir das Objekt in ein (TKey, TValue) dekonstruiert. Dadurch war es dann einfach möglich alle Werte zu durchlaufen, auch wenn ich die Schlüssel gar nicht brauche:

    foreach(var (_, value) in dict){
     //...
    }

    Sicher ein recht seltener Fall, ich finde es so aber schöner als ständig auf Key und Value zugreifen zu müssen.

    Für mich ist Deconstruct ein Feature was zwar existiert und man in Sonderfällen auch nutzen kann, aber meistens zu vermeiden ist. Genauso wie Erweiterungsmethoden (mit einigen Ausnahmen) und Tupeln in public APIs.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    Freitag, 10. März 2017 11:09
    Moderator
  • Hallo,

    wo findet man eine Dokumentation zu ValueTupel und Deconstruct(). Die MSDN liefert mir zu 0 Ergebnisse.

    Gruß
    Heiko

    Freitag, 10. März 2017 15:32
  • hab' was gefunden:

    tuple-deconstruction-in-c-7

    Freitag, 10. März 2017 15:51
  • Danke für deine Antwort, bin dann mal gespannt, wie sich die "offizielle" Version von System.ValueTuple in .NET Framework 4.7... verhält.

    Erweiterungsmethoden haben ja zu mindestens den Vorteil, dass sie nicht implizit aufgerufen werden (außer in deinem Fall als Deconstruct(...).

    Hätte in deinem Beispiel nicht auch

    foreach(var v in d.Values)
    {
      ...
    }

    gereicht?

    Gruß

    Ralf

    Montag, 13. März 2017 10:27
  • Hallo Ralf,

    zu meinem Beispiel:

    deine Version hätte zwar gereicht, aber ich brauchte eben nur das jeweilige Value von v weswegen ich es schöner fand es zu dekonstruktieren.

    Das mag sicher Ansichtssache sein, aber ich sortiere gerne frühzeitig unnötige Daten wie den Key aus.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    Montag, 13. März 2017 22:43
    Moderator
  • ...Das mag sicher Ansichtssache sein, aber ich sortiere gerne frühzeitig unnötige Daten wie den Key aus.

    ...

    Das kann ich verstehen und nachvollziehen. Was nicht gebraucht wird, muss auch nicht erzeugt werden. Das sollte den Code schneller und sicherer machen.

    Aber würde

    ...
    Dictionary<int, float> d = new Dictionary<int, float>();
    ...
    foreach(var f in d.Values)
    {
        ....
    }
    

    das nicht auch tun (hab mir den IL-Code jetzt nicht angeschaut)?

    f ist vom Typ float, die Key-Werte tauchen nicht auf.

    Gruß

    Ralf

    Dienstag, 14. März 2017 11:39
  • Hallo Ralf,

    da hast du recht. An die Values-Eigenschaft habe ich in dem Moment nicht gedacht. Grundlegend geht das aber mit jedem Typ. Angenommen du hast eine List<KeyValuePair<TKey, TValue>>, dann gebe es kein Values. Wobei ein Linq-Aufruf an dieser Stelle vielleicht schöner wäre.

    Schneller wird es übrigens durch einen Deconstructor nicht, sogar eher im Gegenteil. Da sieht man ein anderes Problem: Guter Code ist leider oftmals langsamer. Wobei man ihn mehr optimieren könnte - da müsste aber der Compiler besser werden.

    Um so mehr ich darüber nachdenke: Über mathematisch angehauchte Situationen hinaus würde ich den Deconstructor wohl auch nicht nutzen.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    Dienstag, 14. März 2017 15:20
    Moderator
  • ...Um so mehr ich darüber nachdenke: Über mathematisch angehauchte Situationen hinaus würde ich den Deconstructor wohl auch nicht nutzen...

    So geht es mir ja auch. Einen richtig sinnvollen Einsatz von Deconstruct kann ich noch nicht erkennen.

    Also mal abwarten, Tee trinken und schauen, ob die geistige Erleuchtung noch kommt. Irgendetwas müssen sie sich doch dabei gedacht haben.

    Gruß und vielen Dank fürs Mitdrübernachdenken
    Ralf

    Dienstag, 14. März 2017 17:16
  • Hallo Ralf,

    Danke für deine Antwort, bin dann mal gespannt, wie sich die "offizielle" Version von System.ValueTuple in .NET Framework 4.7... verhält.

    .NET Framework 4.7 wurde veröffentlicht und enthält die ValueTuple-Struktur. Wurde das Windows 10 Creators Update installiert? Hast Du es mit System.ValueTuple ausprobiert?

    Gruß,
    Dimitar


    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Dienstag, 18. April 2017 08:50
    Administrator
  • Ist es tatsächlich so, dass ohne Windows 10 Creators Update das .NET Framework 4.7 in Visual Studio 2017 nicht zur Verfügung steht?

    Aktuell muss das Creators Update bei mir bis Mitte Mai warten.

    Gruß
    Ralf

    Freitag, 21. April 2017 11:01