Benutzer mit den meisten Antworten
Wertetyp an Lambda-Ausdruck übergeben

Frage
-
Hallo Community,
ich würde gerne per Lambda-Ausdruck eine List(Of Point) ändern. Hier ein nicht funktionierdes Beispiel:
Dim pts As New List(Of Point) '... pts.ForEach(Sub(pt) pt = New Point(pt.X, -pt.Y) End Sub)
///
Das funktioniert so natürlich nicht, weil es sich bei Point um einen Wertetypen handelt und die Übergabe ByVal geschieht. Nun dachte ich mir, alles kein Problem, ubergibst Du den Parameter einfacher ByRef. Also pts.ForEach(Sub(ByRef pt)... . Das geht aber nicht, da so die Signatur nicht passt.
Gib es nun eine Möglichkeit trotz dem die Referenz auf den Parameter zu erhalten?
Ich weiß das man das nomalerweise per Index erledigt, aber obiges Beispiel ist halt nur ein Beispiel.
Danke für jeden Tipp
--
Gruß Scotty
- Bearbeitet Robert BreitenhoferModerator Dienstag, 18. Mai 2010 14:53 Formatierung
Antworten
-
Ok, ich habe eine Lösung gefunden:
Ich habe ein Klasse RefPoint erstellt. Diese hat nur einem Member Value As Point. Meine Liste ist jetzt eine List(Of RefPoint). Wenn ich jetzt den Lambda-Ausdruck aufrufe erhalte ich eine Referenz auf das Objekt vom Typ RefPoint und kann die Value ändern. Kleiner positiver Nebeneffekt: Wenn ich nun eine Änderung ausführe(so wie in meinem Beispiel), dann ist diese Variante ca. 3mal so schnell als würde man die Werte per Index ändern. Auf die Idee bin ich übrigens über den Nullable Value Typ System.Nullable(Of Point) oder kurz Point? gekommen .Nullable ist aber selber auch eine Structure deswegen funktionierte das nicht direkt.
--
Gruß Scotty
- Als Antwort markiert Karsten Sosna Samstag, 8. Mai 2010 06:10
Alle Antworten
-
Ok, ich habe eine Lösung gefunden:
Ich habe ein Klasse RefPoint erstellt. Diese hat nur einem Member Value As Point. Meine Liste ist jetzt eine List(Of RefPoint). Wenn ich jetzt den Lambda-Ausdruck aufrufe erhalte ich eine Referenz auf das Objekt vom Typ RefPoint und kann die Value ändern. Kleiner positiver Nebeneffekt: Wenn ich nun eine Änderung ausführe(so wie in meinem Beispiel), dann ist diese Variante ca. 3mal so schnell als würde man die Werte per Index ändern. Auf die Idee bin ich übrigens über den Nullable Value Typ System.Nullable(Of Point) oder kurz Point? gekommen .Nullable ist aber selber auch eine Structure deswegen funktionierte das nicht direkt.
--
Gruß Scotty
- Als Antwort markiert Karsten Sosna Samstag, 8. Mai 2010 06:10
-
Nachtrag:
Habe meine Klasse eben mal zu einem kleinen fixen universell einsetzbaren Helfer um gestaltet.
Public Class RefType(Of T)
Public Value As T
Public Sub New(ByVal value As T)
Me.Value = value
End Sub
End Class
Wer es mal austesten will, hier meine Test Routine:
Dim pts1 As New List(Of RefType(Of Point))
For n = 0 To 4999999
pts1.Add(New RefType(Of Point)(New Point(n, n)))
Next
Dim stpw As New System.Diagnostics.Stopwatch
stpw.Start()
pts1.ForEach(Sub(pt)
pt.Value.Y = -pt.Value.Y
End Sub)
stpw.Stop()
Console.WriteLine("5.000.000 elements via RefType : {0}ms", stpw.ElapsedMilliseconds)
Dim pts2 As New List(Of Point)
For n = 0 To 4999999
pts2.Add(New Point(n, n))
Next
stpw.Reset()
stpw.Start()
For i = 0 To pts2.Count - 1
pts2(i) = New Point(pts2(i).X, -pts2(i).Y)
Next
stpw.Stop()
Console.WriteLine("5.000.000 elements via Index : {0}ms", stpw.ElapsedMilliseconds)
Die Zeiten bei mir:
5.000.000 elements via RefType : 76ms
5.000.000 elements via Index : 211ms
PS: Hoffe diesmal sieht das mit der Formatierung besser aus.
--
Gruß Scotty
-
Hi Karsten,du vergleichst hier völlig unterschiedliche Technologien und das Ergebnis ist dementsprechend ohne sinnvolle Aussage.a) pts1.ForEach(Sub(pt)
pt.Value.Y = -pt.Value.Y
End Sub)
In den vorhandenen Objekten werden Werte geändert.b) For i = 0 To pts2.Count - 1pts2(i) = New Point(pts2(i).X, -pts2(i).Y)
Next
Es werden neue Objekte instanziiert. Die Speicherplatzzuweisung dauert natürlich länger als eine einfache Änderungen eines Wertes auf einem bereits zugewiesenen Speicherplatz.
--
Viele GruessePeter -
Hallo Peter,
anscheinend hast Du nicht verstanden, was ich aufzeigen wollte. Weder in einer ForEach noch Index-Schleife kann man die Werte X und Y Werte nicht direkt zu weisen. Vielleicht hast Du nicht gesehen, dass pts2 eine List(Of Point) ist. So etwas funktioniert also nicht:
b) For i = 0 To pts2.Count - 1pts2(i).Y = -pts2(i).Y
Next
Fehler 1 Der Ausdruck ist ein Wert und kann nicht als Ziel einer Zuweisung verwendet werden.
Die einzige mögliche Zuweisung kann nur wie in meinem Beispiel geschen. Und in einer ForEach-Schleife geht es gar nicht, da die Schleifenvariable ByVal übergeben wird.
Du hast in sofern Recht dass es 2 verschiedene Techniken sind, aber genau hier entsteht der Vergleich. Die RefType-Variante ist schneller als eine normale List(of Point) zu benutzen und dort die Werte via Index zu ändern. Selbst wenn ich anstelle einer List(Of Point) eine List(Of RefType(Of Point)) benutze hätte,ist der Weg über den Index langsamer. Bei mir knapp um den Faktor 2. Ich komme bei ForEach auf ca. 75ms, mit Index auf ca. 140ms. Warum das so ist kann ich nicht sagen.
Was den Test mit den 5Mio. Elementen angeht, so sind dass für mich noch relativ kleine Listen. Fakt ist nur das diese Listen mehrfach manipuliert werden und das muss so schnell gehen wie nur möglich. Deswegen findet ein Vergleichstest in Bezug auf die Geschwindigkeit statt. Dazu kann ich eine "herkömmliche" Variante durchaus mit einer "spezialisierten" Variante vergleichen.
--
Gruß Scotty
-
Hi Karsten,ich bekomme keine Probleme bzw. habe das Problem nicht verstanden. Hier mal meine Demo:
Module Module1<o:p></o:p>
<o:p> </o:p>
Sub Main()<o:p></o:p>
'<o:p></o:p>
Dim pts1 As New List(Of RefType(Of Point))<o:p></o:p>
For n = 0 To 4999999<o:p></o:p>
pts1.Add(New RefType(Of Point)(New Point(n, n)))<o:p></o:p>
Next<o:p></o:p>
Dim stpw As New System.Diagnostics.Stopwatch<o:p></o:p>
stpw.Start()<o:p></o:p>
pts1.ForEach(Sub(pt)<o:p></o:p>
pt.Value.Y = -pt.Value.Y<o:p></o:p>
End Sub)<o:p></o:p>
stpw.Stop()<o:p></o:p>
Console.WriteLine("5.000.000 elements via RefType : {0}ms", stpw.ElapsedMilliseconds)<o:p></o:p>
'<o:p></o:p>
Dim pts2 As New List(Of Point)<o:p></o:p>
For n = 0 To 4999999<o:p></o:p>
pts2.Add(New Point(n, n))<o:p></o:p>
Next<o:p></o:p>
'<o:p></o:p>
stpw.Reset()<o:p></o:p>
stpw.Start()<o:p></o:p>
For i = 0 To pts2.Count - 1<o:p></o:p>
pts2(i) = New Point(pts2(i).X, -pts2(i).Y)<o:p></o:p>
Next<o:p></o:p>
stpw.Stop()<o:p></o:p>
Console.WriteLine("5.000.000 elements via Index : {0}ms", stpw.ElapsedMilliseconds)<o:p></o:p>
'<o:p></o:p>
Dim pts3 As New List(Of Point)<o:p></o:p>
For n = 0 To 4999999<o:p></o:p>
pts3.Add(New Point(n, n))<o:p></o:p>
Next<o:p></o:p>
'<o:p></o:p>
stpw.Reset()<o:p></o:p>
stpw.Start()<o:p></o:p>
For i = 0 To pts2.Count - 1<o:p></o:p>
pts2(i).Y = -pts2(i).Y<o:p></o:p>
Next<o:p></o:p>
stpw.Stop()<o:p></o:p>
Console.WriteLine("5.000.000 elements direct : {0}ms", stpw.ElapsedMilliseconds)<o:p></o:p>
<o:p> </o:p>
' Anzeige anhelten<o:p></o:p>
Console.ReadKey()<o:p></o:p>
End Sub<o:p></o:p>
<o:p> </o:p>
End Module<o:p></o:p>
<o:p> </o:p>
Public Class RefType(Of T)<o:p></o:p>
Public Value As T<o:p></o:p>
Public Sub New(ByVal value As T)<o:p></o:p>
Me.Value = value<o:p></o:p>
End Sub<o:p></o:p>
End Class<o:p></o:p>
<o:p> </o:p>
Public Class Point<o:p></o:p>
Public Sub New(ByVal x As Double, ByVal y As Double)<o:p></o:p>
Me.X = x<o:p></o:p>
Me.Y = y<o:p></o:p>
End Sub<o:p></o:p>
Public Property X As Double<o:p></o:p>
Public Property Y As Double<o:p></o:p>
End Class
--
Viele GruessePeter<o:p></o:p>
-
Dim pts2 As New List(Of Point)
For i = 0 To pts2.Count - 1
pts2(i).Y = -pts2(i).Y
Next
Public Class Point
Public Sub New(ByVal x As Double, ByVal y As Double)
Me.X = x
Me.Y = y
End Sub
Public Property X As Double
Public Property Y As Double
End Class
Hallo Peter,
ja so schon, Du hast ja auch eine neue Klasse erstellt. Ich habe aber die System.Drawing.Point-Structure benutzt und damit ist die Zuweisung nicht möglich. Mit einer solchen speziellen Klasse kann man den Wert auch via ForEach ändern, da es sich um einen Referenztyp handelt. Mit der Struktur ist das nicht möglich,da es sich um einen Wertetyp handelt und beim Durchlaufen der ForEach-Schleife nur Kopien geliefert werden. Bei einem Referenztyp wird zwar auch nur eine Kopie geliefert, jedoch ist dies eine Kopie der Referenz und somit kann man das Objekt auch beeinflussen. Aber das ist Dir sicherlich bekannt.
--
Gruß Scotty
-
Ich habe aber die System.Drawing.Point-Structure benutztHi Karsten,daran hatte ich nicht gedacht, da du es nicht geschrieben hast. Ich bin wegen den von dir genutzten Console.WriteLine von einer Konsolen-Anwendung ausgegangen und da fehlt standardmäßig der System.Drawing-Namensraum.Und warum kannst du nicht die Strukturen nutzen? Hier mal eine Demo, wie das noch viel schneller geht, wenn man direkt in die Felder der Struktur schreibt:Imports System.drawingModule Module1Sub Main()
'
Dim pts1 As New List(Of RefType(Of Point))
For n = 0 To 4999999
pts1.Add(New RefType(Of Point)(New Point(n, n)))
Next
Dim stpw As New System.Diagnostics.Stopwatch
stpw.Start()
pts1.ForEach(Sub(pt)
pt.Value.Y = -pt.Value.Y
End Sub)
stpw.Stop()
Console.WriteLine("5.000.000 elements via RefType : {0}ms", stpw.ElapsedMilliseconds)
'
Dim pts2 As New List(Of Point)
For n = 0 To 4999999
pts2.Add(New Point(n, n))
Next
'
stpw.Reset()
stpw.Start()
For i = 0 To pts2.Count - 1
pts2(i) = New Point(pts2(i).X, -pts2(i).Y)
Next
stpw.Stop()
Console.WriteLine("5.000.000 elements via Index : {0}ms", stpw.ElapsedMilliseconds)
'
Dim pts3 As New List(Of Point)
For n = 0 To 4999999
pts3.Add(New Point(n, n))
Next
'
stpw.Reset()
stpw.Start()
For i = 0 To pts2.Count - 1
Dim p = pts3(i)
p.Y = -p.Y
Next
stpw.Stop()
Console.WriteLine("5.000.000 elements direct : {0}ms", stpw.ElapsedMilliseconds)' Anzeige anhelten
Console.ReadKey()
End SubEnd ModulePublic Class RefType(Of T)
Public Value As T
Public Sub New(ByVal value As T)
Me.Value = value
End Sub
End Class
--
Viele GruessePeter -
Und warum kannst du nicht die Strukturen nutzen? Hier mal eine Demo, wie das noch viel schneller geht, wenn man direkt in die Felder der Struktur schreibt:
Hallo Peter weils das nicht geht. Schau hier:
Dim pts As New List(Of Point)
pts.Add(New Point(0, 0))
Dim p = pts(0)
p.X = 10
Console.WriteLine(p.X)
Console.WriteLine(pts(0).X)
Ausgabe:
10
0
Eine Struktur ist ein Wertetyp. Mit der Zuweisung p = pts(0) erhält man nur ein Kopie. Wenn das bei Dir funktioniert, befürchte ich Du hast Deine Point-Klasse noch im Projekt. Der reine Import des System.Drawing-Namespace reicht nicht aus. Entferne mal die Klasse oder deklariere Deine List so:
Dim pts As New List(Of System.Drawing.Point)
Dann wirst Du sehen dass das nicht geht.
P.S.: Was das Benutzen der Console-Klasse angeht, so ist das Macht der Gewohnheit.
--
Gruß Scotty
-
Hi Karsten,wenn die Daten wieder in der Liste gebraucht werden, müssen sie zurückgeschrieben werden, was immer noch schneller geht als eine Neuzuweisung von Speicherplatz:Dim pts As New List(Of Point)
pts.Add(
New Point(0, 0)) Dim p0 = pts(0)p0.X = 10
pts(0) = p0
Console.WriteLine(p0.X) Console.WriteLine(pts(0).X)"Karsten Sosna" schrieb im Newsbeitrag news:c7abded8-e6c7-411c-9950-d407521c1e72...Und warum kannst du nicht die Strukturen nutzen? Hier mal eine Demo, wie das noch viel schneller geht, wenn man direkt in die Felder der Struktur schreibt:
Hallo Peter weils das nicht geht. Schau hier:
Dim pts As New List(Of Point)
pts.Add(New Point(0, 0))
Dim p = pts(0)
p.X = 10
Console.WriteLine(p.X)
Console.WriteLine(pts(0).X)
Ausgabe:
10
0
Eine Struktur ist ein Wertetyp. Mit der Zuweisung p = pts(0) erhält man nur ein Kopie. Wenn das bei Dir funktioniert, befürchte ich Du hast Deine Point-Klasse noch im Projekt. Der reine Import des System.Drawing-Namespace reicht nicht aus. Entferne mal die Klasse oder deklariere Deine List so:
Dim pts As New List(Of System.Drawing.Point)
Dann wirst Du sehen dass das nicht geht.
P.S.: Was das Benutzen der Console-Klasse angeht, so ist das Macht der Gewohnheit.
--
Gruß Scotty
-
wenn die Daten wieder in der Liste gebraucht werden, müssen sie zurückgeschrieben werden, ...
Hallo Peter,
wenn ich das nicht bräuchte, würde ich nicht solche Wege gehen. Schau Dir doch noch mal das Eingangsposting an. Wozu bräuchte ich sonst einen Verweis? Ich hatte auch etwas später geschrieben: "Fakt ist nur das diese Listen mehrfach manipuliert werden ...".
Das ist leider zu weit ausgeartet und dabei ist meine eigentliche Anfrage untergegangen. Es ging um den Parameter eines Lambda-Ausdrucks und den kann man nicht manipulieren, wenn es sich um eine Wertetyp handelt.
Da ich glaube dass es an dieser Stelle keinen Sinn mehr hat hier gegenseitig Beispielcode auszutauschen und ich ja meine Lösung(Lösungsansatz) schon gefunden habe, können wir es an dieser Stelle gerne beenden.
--
Gruß Scotty
-
Hi Karsten,
die NNTP-Bridge hatte mehrmals mein Windows abgeschossen (100 % CPU). Letztes Mal war auch noch der Finger abgerutscht und das Posting zu früh losgeschicktwenn die Daten wieder in der Liste gebraucht werden, müssen sie zurückgeschrieben werden, was immer noch schneller geht als eine Neuzuweisung von Speicherplatz:Dim pts As New List(Of Point)
pts.Add(New Point(0, 0))
Dim p0 = pts(0)
p0.X = 10
pts(0) = p0
Console.WriteLine(pts(0).X)Übrigens hat dein Programmschnipsel das gleiche Problem. Schau dir mal an:
Dim pts1a As New List(Of RefType(Of Point))
pts1a.Add(New RefType(Of Point)(New Point(20, 20)))
Console.WriteLine(pts1a(0).Value.X)
pts1a(0).Value.Y = -pts1a(0).Value.Y
Console.WriteLine(pts1a(0).Value.X)Damit bringt dein geposteter Meßaufbau kein bracuhbaren Ergebnisse.
--
Viele GruessePeter
-
Übrigens hat dein Programmschnipsel das gleiche Problem. Schau dir mal an:
Dim pts1a As New List(Of RefType(Of Point))
pts1a.Add(New RefType(Of Point)(New Point(20, 20)))
Console.WriteLine(pts1a(0).Value.X)
pts1a(0).Value.Y = -pts1a(0).Value.Y
Console.WriteLine(pts1a(0).Value.X)Damit bringt dein geposteter Meßaufbau kein bracuhbaren Ergebnisse.
Hallo Peter,
mein Codebeispiel mach genau das was es soll. Das macht Deins übrigens auch. Jedoch wenn Du Y änderst dann solltest Du Dir auch Y anschauen und X. ;=)
Damit sind wohl Deine beiden Aussagen hinfällig.
--
Gruß Scotty