Fragensteller
Überladen von Deconstruct

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).
- Bearbeitet Ralf_Ellersiek Donnerstag, 9. März 2017 10:20
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 2017Gruß,
DimitarBitte 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.
-
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.
-
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 -
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
- Bearbeitet Ralf_Ellersiek Freitag, 10. März 2017 07:43
-
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 -
hab' was gefunden:
-
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
-
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 -
...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
-
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 -
...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 -
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ß,
DimitarBitte 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.