Hallo,
Ich rufe in einer Methode eine Methode auf. Wie kann ich dor ein ref Parameter verwenden? Bekomme dort immer den Fehler:
Ein ref- muss immer eine zuweisbare Variable sein.
Schlüsselsammeln(ref UsersKey.OpenSubKey(Untereinträge[i], true)); // Hier der Error wegen dem ref
Insgesammt schaut der Code so aus:
...
Schlüsselsammeln(UsersKey);//Aufruf
}
public static void Schlüsselsammeln(RegistryKey UsersKey)
{
for..{
Schlüsselsammeln(UsersKey.OpenSubKey(Untereinträge[i], true));
}
}
Habs so probiert:
Schlüsselsammeln ref UsersKey = UsersKey.OpenSubKey(Untereinträge[i], true);
Schlüsselsammeln(UsersKey)
Funktioniert aber leider nicht.
Habt ihr ne Idee?
mfg martin
Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp
Hi!
Ein ref- muss immer eine zuweisbare Variable sein.
Wenn du das Fehler bekommst, wird es auch so sein. 🙂 Wenn es irgendwie gehen würde, würdest du den fehler nicht bekommen. aber du bekommst ihn.
Ist de facteo ja auch etwas sinnlos.
Und eine Kopie im Stack (ohne ref) wäre ja hier auch nicht so schlimm, oder?
der Marcel
:] 😄Der größte Fehler eines modernen Computers sitzt meist davor 😁 :]
ich zitiere mal die hilfe
Das ref-Schlüsselwort bewirkt, dass Argumente als Verweis übergeben werden. Dies hat zur Folge, dass alle Änderungen am Methodenparameter in diese Variable übernommen werden, sobald die Steuerung wieder an die aufrufende Methode übergeben wird. Um einen ref-Parameter zu verwenden, müssen sowohl die Methodendefinition als auch die aufrufende Methode explizit das ref-Schlüsselwort verwenden. Beispiel:
class RefExample { static void Method(ref int i) { i = 44; } static void Main() { int val = 0; Method(ref val); // val is now 44 } }
Ein Argument, das an einen ref-Parameter übergeben wird, muss zunächst initialisiert werden. Dies stellt einen Unterschied zu out dar, denn das Argument dieses Parameters muss nicht explizit initialisiert werden, bevor es übergeben wird. (Informationen finden Sie unter out.)
um ref zu verwenden mußt du vorher eine variable definieren. siehe "int val = 0"
erst dann kansnt du sie mit "ref val" übergeben. initialisierst du sie nicht, fkt das ganze nicht da du ja sozusagen eine adresse mitgibst.
beim aufruf mußt du in der parameterliste "ref val" angeben, die methodendeklaration muß auch das "ref" wort beinhalten.
wie die übergabe und deklaration der methode aussieht siehst du oben 🙂
Hallo sbrain,
Mir ist nicht ganz klar, was da nun per ref übergeben werden soll?
OpenSubKey liefert doch ein RegistryKey Objekt zurück.
Das Ergebnis wird keiner Variablen zugewiesen, die durch den Aufruf von Schlüsselsammeln ihren Wert verändern könnte.
Warum ein REF ?
Wieso nicht einfach ohne REF den Key übergeben und sammeln?
Original von cadi
Hallo sbrain,Mir ist nicht ganz klar, was da nun per ref übergeben werden soll?
OpenSubKey liefert doch ein RegistryKey Objekt zurück.
Das Ergebnis wird keiner Variablen zugewiesen, die durch den Aufruf von Schlüsselsammeln ihren Wert verändern könnte.
Warum ein REF ?Wieso nicht einfach ohne REF den Key übergeben und sammeln?
Hallo,
vorerst einmal danke für die zahlreichen Antworten. Ich möchte in einer weiteren Methode RegistryKey UsersKey weiter bearbeiten und das kann ich nur entweder mit ref oder out.
mfg martin
Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp
Hallo sbrain,
dann würde ich es so machen:
RegKey currentKey = UsersKey.OpenSubKey(Untereinträge[i], true);
Schlüsselsammeln(currentKey);
IrgendeineFunktion(currentKey); // hier die 2. funktion die den selben key benutzen soll
Nein, da sind ref und out völlig falsch , weil die eh nur mit Wertetypen Sinn machen, du hast es aber mit nem Referenztyp zu tun, da brauchst du kein ref angeben. Machs so wie cadi es vorgeschlagen hat, das ist die gängige Vorgehensweise.
Baka wa shinanakya naoranai.
Mein XING Profil.
Hallo,
danke funktioniert. Ich hab vorher die 2te Methode nicht in der Rekursive Methode gestartet, da ich dachte, dass die 2te Methode zu früh gestartet wird und dann zu wenig Werte geliefert bekommt. Aber jetzt ist mir klar das die 2teMethode so oft gestartet wird wie nötig um alle Werte in der zweiten Methode zu bearbeiten.
mfg martin
Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp
Hallo talla,
unabhängig von dem hier diskutierten Problem gibt es durchaus sinnvolle Anwendungen von ref mit Referenztypen.
herbivore
Ja okay, weil die Referenzen die verwendet werden, eigentlich auch nur Kopien der Referenzen sind 😉 Sprich wenn man die eigentliche Referenz an sich ändern will brauch man das, oder welchen Fall meinst du?
Baka wa shinanakya naoranai.
Mein XING Profil.
Hallo talla,
ja, genau den Fall. Bei out statt ref wird es noch klarer.
herbivore
Ich perönlich finde ref auch als Dokumentation ganz gut. Deswegen finde ich auch gut, dass man beim Methodenaufruf auch 'ref' schreiben muss. So ist klar, dass das übergebene Objekt geändert wird.
A wise man can learn more from a foolish question than a fool can learn from a wise answer!
Bruce Lee
Populanten von Domizilen mit fragiler, transparenter Außenstruktur sollten sich von der Translation von gegen Deformierung resistenter Materie distanzieren!
Wer im Glashaus sitzt, sollte nicht mit Steinen werfen.
Hallo progger,
da muss ich dir widersprechen: ref hat nicht damit zu tun, dass das Objekt geändert wird. Es hat was damit zu tun, dass die Referenz auf das Objekt geändert wird (bzw. werden kann). Das eine hat mit dem anderen nichts zu tun und deshalb sollte man ref auf keinen Fall dazu benutzen, um auszudrücken, dass das Objekt geändert wird (bzw. werden kann).
Objekte sind Objekte und Variablen sind Variablen. ref sagt etwas über Variablen aus, nicht über Objekte.
herbivore
Hallo Progger,
hättest du ValueTypes (und nicht Objekte) dann wäre deine Aussage korrekt.
Bei Objekten wird (kann geändert werden...) nicht das Objekt geändert (also die properties) sondern gleich die Referenz auf ein völlig neues Objekt gesetzt.
Die Properties eines als Parameter übergebenen Objektes kann man aber auch ohne ref jederzeit in ändern.
Original von cadi
Bei Objekten wird (kann geändert werden...) nicht das Objekt geändert (also die properties) sondern gleich die Referenz auf ein völlig neues Objekt gesetzt.
Nein 😁 Es wird ne neue Referenz auf das schon bestehende Objekt gesetzt. Das ist nen Unterschied.
Ich versuche das mal zu erläutern 🙂
Es gibt den Stack und den Heap. Des sind verschiedene Speicherbereiche die sich in .Net aber ähnlich verhalten, im Gegensatz zu C++ z.b. wo die intern ganz anders arbeiten.
Wertetypen werden in den Stack gepackt. Deren Größe ist vorher genau festgelegt und ändert sich nicht. So wird immer der passend große Speicherbereich am Ende des Stacks genommen und dort der Wert reingeschrieben. Das ganze funktioniert durch des Lifo Prinzip. Also die Werte die zuletzt raufgepackt wurden, sind auch die Werte die zuerst außerhalb des Scopes geraten und so wieder entfernt werden können. Außerdem gibts den Stackpointer der immer das Ende der letzten Variable anzeigt, beim Entfernen einer Variable wird einfach der Stackpointer um die Größe der Variable zurückgesetzt(eigentlich erhöht, da der Stack von hohen Adressen zu niedrigen arbeitet). Deshalb kann man auch auf den Stack recht zügig arbeiten, da steckt nicht viel Verwaltungsaufwand dahinter.
Nun zum Heap. Wir haben ne Klasse mit dem Namen "Test".
Bei
Test meinTest;
werden jetzt auf dem Stack! 4 Byte für den Pointer auf ein Test Objekt reserviert.
Mit
meinTest = new Test();
wird jetzt der Platz für das Objekt auf dem Heap reserviert und in die Variable meinTest wird die Adresse auf dieses Objekt gesetzt(und dann wird der Konstruktor aufgerufen). Wo der Speicher reserviert wird unterschiedet sich jetzt bei .Net von einem unmanaged Heap, aber darauf geh ich am Ende ein, will erstmal zum Thema zurück.
meinTest ist nicht das Objekt meiner Test Klasse! Sondern nur ein Pointer auf das Objekt. Wenn ich nun in einer Funktion als Parameter ein ReferenzType übergebe, wird eine Kopie der Referenz auf dieses Objekt erstellt und damit arbeitet man nun in der Funktion.(Sprich es werden auf dem Stack wieder 4 Byte für eine Adresse auf das Objekt reserviert und dort wird dann die Adresse meines Objektes zugewiesen). Gebe ich aber ref mit an, wird wirklich mit der Referenz gearbeitet die ich auch übergebe.
Bei Wertetypen wird mit ref halt wirklich mit dem Wert an der übergebenen Adresse gearbeitet, statt den Wert neu aufn Stack zu kopieren und dann damit zu arbeiten.
Hoffe meine Erklärung ist net ganz zu umständlich.
Was ich zum Heap noch loswerden wollte: Der Heap ist in .Net eigentlich auch nur nen Stack 🙂 Die Größe von Referenztypen ist ja nicht im vorraus bekannt und wenn der GC jetzt Objekte entfernt, entstehen Lücken und man könnte nicht hintereinander Speicher reservieren wie beim Stack. Aufgabe des GC ist jetzt auch den Heap zu kompaktieren, sprich die Objekte werden alle linear ohne Lücken angeordnet und der GC updatet die Adressen der Referenzen aufn Stack. Dann ist alles wieder schön hintereinander und neuen Speicher anfordern geht ruck zuck. Das ist auch der Grund warum ein managed Heap(also mit GC) um einiges schneller ist als ein unmanaged Heap wo immer erst ne Stelle gesucht werden muss, wo ein Objekt reinpasst.
Was mir grad noch einfällt wo ich eh schon so nen kleinen Rundumschlag im MemoryManagment von .Net mache. Die Sache mit Objekte auf null setzen =) Wird ja gerade von Anfängern gerne missverstanden.
Das geht gar nicht! Objekte kann man niemals auf null setzen. Man setzt mit einer Zuweisung wie
meinTest = null;
nur die Adresse der Referenz aufm Stack auf null. Das Objekt aufm Heap bleibt weiter bestehen, da kann man mittelbar gar nichts dran ändern. Aufm Stack werden Variablen gelöscht wenn sie außerhalb des Scopes geraten, aufn Heap löscht ganz alleine der GC! Auch mit Finalizer kann der User nicht deterministisch aufm Heap rumpfuschen. Der GC guckt durch ob noch Referenzen auf ein Objekt vorhanden sind, falls nein wird geschaut ob nen Finalizer vorhanden ist. Ist nun ein Finalizer vorhanden ists schlecht, weil dann wird der ausgeführt und das Objekt kann erst beim nächsten Durchlauf des GC entfernt werden, oder wenn weder Referenzen vorhanden sind auf ein Objekt, noch ein Finalizer, wird der Speicherplatz freigegeben, die Lücke wird durch das kompaktieren des Heaps gefüllt und das wars dann 😉 Erst dann ist der Speicherplatz wirklich freigegeben.
Soo, auch wenns bissle mehr war als des Thema verlangt hat - Fragen wie Heap und Stack funktionieren und zusammenhängen gabs ja auch schon öfter und das mit dem ref kann man auch nur verstehen wenn man weiß, wie was m Speicher abgelegt wird. Und wenn jetzt nochmal sowas auftritt, kann ich wenigstens hierhin verweisen, hab keine Lust sowas nochmal zu schreiben 😉
Auch hoffe ich das ich niemanden die Illusion genommen hab, in .Net muss man nichts mehr mit Zeigern zu tun haben 🙂
Baka wa shinanakya naoranai.
Mein XING Profil.
Kleine Korrektur: Das Setzen einer Referenz auf Null kann (!) durchaus Sinn machen, nämlich dann, wenn der Zeitpunkt des Verlassen des Scopes noch in weiter Ferne liegt. Das kann z.B. dann passieren, wenn man auf ein Sync-Objekt wartet. Ist die Referenz eigentlich unbenutzt und wird nicht auf Null gesetzt, kann durch einen Threadwechsel und die Auslösung des GCs unnötigerweise das Objekt in die nächste Generation geschoben werden. Vor allem in Applikation mit vielen Threads ein Thema.
Hallo talla,
das ist eine umfassende Darstellung des Themas. Auch und gerade, weil du schreibst
Und wenn jetzt nochmal sowas auftritt, kann ich wenigstens hierhin verweisen
hier ein paar Präzisierungen, denen du dir vermutlich bewusst bist, die aber nicht explitzit in deinem Text auftauchen:
Es wird ne neue Referenz auf das schon bestehende Objekt gesetzt. Das ist nen Unterschied.
Richtig, das ist ein Unterschied, aber das ist nicht das, was bei der Paramterübergabe mit ref passiert, die Cadi beschreibt. Durch ref wird eine Referenz auf die Referenz übergeben. Nicht ein zweite Referenz auf das Objekt.
Wertetypen werden in den Stack gepackt.
So pauschal stimmt das nicht. Für die Parameterübergabe ist es richtig, wenn man aber ein Objekt erzeugt, das Werte enthält, dann liegen das Objekt (und damit seine Werte) auf dem Heap.
Das ist auch der Grund warum ein managed Heap(also mit GC) um einiges schneller ist als ein unmanaged Heap wo immer erst ne Stelle gesucht werden muss, wo ein Objekt reinpasst.
Eine freie Stelle zu finden, geht bei managed und unmanaged gleich schnell. Ansonsten ist es eher anderesherum. Der managed heap ist langsamer (es wird ja mehr getan), dafür fragmentiert er nicht.
Man setzt mit einer Zuweisung wie nur die Adresse der Referenz aufm Stack auf null.
Ich finde das missverständlich: Ich würde sagen, die Referenz selbst (oder von mir aus den Inhalt der Referenz) wird auf null gesetzt. Noch einfacher finde ich aber: Objekte sind Objekte und Variablen sind Variablen; Variablen kann man auf null setzen, Objekte nicht.
Der GC guckt durch ob noch Referenzen auf ein Objekt vorhanden sind, falls nein wird geschaut ob nen Finalizer vorhanden ist.
Präzisierung: Der GC guckt, ob noch "von außen" erreichbare Referenzen vorhanden sind. Zyklischen Strukturen werden von CG freigegeben, obwohl ja auf jedes Objekt noch mindestens eine Referenz besteht, wenn es keine anderen Referenzen mehr gibt.
herbivore
Es war spät. Jetzt wenn ichs nochmal durchlese hätt ich bestimmt einiges anders geschrieben 🙂 Es sollte auch nicht hundertprozentig technisch korrekt sein, deshalb sind da einige Unsauberheiten reingekommen.
Original von herbivore
Es wird ne neue Referenz auf das schon bestehende Objekt gesetzt. Das ist nen Unterschied.
Richtig, das ist ein Unterschied, aber das ist nicht das, was bei der Paramterübergabe mit ref passiert, die Cadi beschreibt. Durch ref wird eine Referenz auf die Referenz übergeben. Nicht ein zweite Referenz auf das Objekt.
Das war mir so nicht bewusst, macht aber natürlich auch Sinn. Glaube was ich geschildert hab, ist doch eh der Fall bei normaler Referenzübergabe oder 🤔
Original von herbivore
Wertetypen werden in den Stack gepackt.
So pauschal stimmt das nicht. Für die Parameterübergabe ist es richtig, wenn man aber ein Objekt erzeugt, das Werte enthält, dann liegen das Objekt (und damit seine Werte) auf dem Heap.
Und Parameter von Funktionen und lokale Variablen werden wieder aufn Stack gepackt, von daher verteilt sich das im Endeffekt recht willkürlich. Deshalb hab ich dass auch außen vor gelassen, verwirrt bestimmt bloß.
Original von herbivore
Das ist auch der Grund warum ein managed Heap(also mit GC) um einiges schneller ist als ein unmanaged Heap wo immer erst ne Stelle gesucht werden muss, wo ein Objekt reinpasst.
Eine freie Stelle zu finden, geht bei managed und unmanaged gleich schnell. Ansonsten ist es eher anderesherum. Der managed heap ist langsamer (es wird ja mehr getan), dafür fragmentiert er nicht.
Okay, das war nen bissle lausig formuliert.
Es stimmt das der managed Heap mehr Verwaltungsaufwand mitbringt, und das sich schlecht auf die Performance auswirken kann. Gegenteilige Effekte sind aber, dass größere Speicherbereiche auf einmal freigegeben werden können(im Gegensatz zu z.b. C++ wo jedes Objekt einzeln freigegeben wird) und die Stackähnliche Arbeitsweise weil er ja kompaktiert wird. Und genau darauf bezog sich jetzt mein "schneller": Erstell mal in C# Millionen von Objekten und in C++ - das allozieren in C# geht viel schneller, weil wirklich einfach Speicher am Ende des Heaps genommen werden kann. Durch das fragmentieren und Speicherlücken suchen auf unmanaged Heaps dauert das wirklich länger. Die sidn in der Hinsicht nicht gleich schnell.
Original von herbivore
Man setzt mit einer Zuweisung wie nur die Adresse der Referenz aufm Stack auf null.
Ich finde das missverständlich: Ich würde sagen, die Referenz selbst (oder von mir aus den Inhalt der Referenz) wird auf null gesetzt. Noch einfacher finde ich aber: Objekte sind Objekte und Variablen sind Variablen; Variablen kann man auf null setzen, Objekte nicht.
Das ist wirklich verdammt blöd formuliert 🙁 Ich änder natürlich nicht die Adresse der Referenz an sich, sondern setzte die Adresse auf die die Referenz zeigt(nämlich auf mein Objekt im Heap) auf null. Ist wie du sagst wie bei ner Variablen, ich setz deren Wert sie einfach auf null(was aber nicht 0 ist gg).
Original von herbivore
Der GC guckt durch ob noch Referenzen auf ein Objekt vorhanden sind, falls nein wird geschaut ob nen Finalizer vorhanden ist.
Präzisierung: Der GC guckt, ob noch "von außen" erreichbare Referenzen vorhanden sind. Zyklischen Strukturen werden von CG freigegeben, obwohl ja auf jedes Objekt noch mindestens eine Referenz besteht, wenn es keine anderen Referenzen mehr gibt.
Stimmt natürlich auch. Aber da fehlen eh noch so einige Kleinigkeiten 😉 Ist ja nur grob die Arbeitsweise von dem ganzen.
Gruß Karsten
Baka wa shinanakya naoranai.
Mein XING Profil.
Hallo talla,
Glaube was ich geschildert hab, ist doch eh der Fall bei normaler Referenzübergabe oder
genau, bei der normalen Übergabe, wird die Referenz auf das zu übergebende Objekt by-value übergeben. Sprich es wird - wie du sagst - auf dem Stack eine Kopie der Referenz erstellt.
Deshalb hab ich dass auch außen vor gelassen, verwirrt bestimmt bloß.
Och, so verwirrend ist das gar nicht. Parameter und lokale Variablen landen auf dem Stack und Objekte (also deren Felder/Instanzvariablen) auf dem Heap.
Erstell mal in C# Millionen von Objekten und in C++ - das allozieren in C# geht viel schneller, weil wirklich einfach Speicher am Ende des Heaps genommen werden kann.
Bist du dir sicher? Und wenn das Erstellen schneller geht, bist du dir sicher, dass auch wirklich das Allokieren selbst schneller geht?
Durch das fragmentieren und Speicherlücken suchen auf unmanaged Heaps dauert das wirklich länger. Die sidn in der Hinsicht nicht gleich schnell.
Das hängt ja alleine von dem Algorithmus für die Freispeicherverwaltung ab. Es gibt ja verschiedene Algorithmen, die das Fragmentieren minimieren sollen, z.B. best-fit (die kleinste Lücke, die groß genug ist) oder worse-fit (die größte lücke). Ich denke aber, dass bei Hauptspeicher eher first-fit (die erste Lücke, die groß genug ist) verwendet wird. Und das ist nicht (oder höchsten kaum) langsamer als wenn man immer am Ende allokieren kann.
herbivore
Original von herbivore
Erstell mal in C# Millionen von Objekten und in C++ - das allozieren in C# geht viel schneller, weil wirklich einfach Speicher am Ende des Heaps genommen werden kann.
Bist du dir sicher? Und wenn das Erstellen schneller geht, bist du dir sicher, dass auch wirklich das Allokieren selbst schneller geht?
Ich denke das stimmt wirklich. In C++ ist die Speicherverwaltung als linked List implementiert, die muss durchlaufen werden, bis ein ausreichend großer Block gefunden wird. Der managed Heap ist in als Stack implementiert, da schnappt man sich einfach den Speicher oberhalb.
Hier übrigens ein ausgezeichneter Artikel zum ganzen Thema:
Original von svenson
Hier übrigens ein ausgezeichneter Artikel zum ganzen Thema:
>
Hab grad leider keine Zeit, scheint aber echt interessant zu sein, wird wieder was zum Lesen für heute Abend 🙂
Baka wa shinanakya naoranai.
Mein XING Profil.