Laden...

Zeiger, anstatt Daten, von value-type kopieren

Erstellt von Bazhosh vor 11 Jahren Letzter Beitrag vor 11 Jahren 3.985 Views
B
Bazhosh Themenstarter:in
9 Beiträge seit 2010
vor 11 Jahren
Zeiger, anstatt Daten, von value-type kopieren

Hallo!

Ich stehe im Moment vor folgendem Problem und frage mich, ob ich das mit .Net lösen kann, oder ob ich C++(/CLI) nutzen muss:

Ich habe mehrere Objekte, welche nacheinander das selbe Byte-Array bearbeiten sollen. Das Problem ist nun, dass Byte ein value-type ist und dabei immer alle Daten kopiert werden, anstatt einfach nur ein Zeiger (wie bei einem reference-type).

Welche Möglichkeiten gibt es, dies mit .Net speicherschonend zu machen?

MfG

1.378 Beiträge seit 2006
vor 11 Jahren

Ein Array ist aber ein Referenztyp und beim Übergeben des Arrays wird daher nur die Referenz auf dieses übergeben und nicht alle Werte extra kopiert.

Lg, XXX

//EDIT: Ansonsten kannst du auch deinen ValueType in einer Klasse kapseln und dieses Objekt dann überall verwenden.

//EDIT²: Letzteres wird dein Problem aber nicht beheben da eine Referenz auf einen Typen vermutlich mehr Speicher verbraucht als ein einfaches byte - Das Sinnvollste werden Arrays sein die du dann immer als "ganzes" übergibst.

W
872 Beiträge seit 2005
vor 11 Jahren

Wenn Du wirklich sehr speicher-schonend arbeiten willst, dann wuerde ich das Byte-Array nur im Konstruktor der Klasse benutzen und dann bei der Implementierung der Properties immer den entsprechenden Teil ausschneiden/in den Value Typ konvertieren.

B
Bazhosh Themenstarter:in
9 Beiträge seit 2010
vor 11 Jahren

Also ich hab noch mal einen kleinen Test gemacht und es ist tatsächlich so, dass das Byte-Array als Referenz übergeben wird.
Das Problem muss wohl in der Bibliothek liegen, die ich nutze.

W
872 Beiträge seit 2005
vor 11 Jahren

Ich verstehe Dein Problem nicht ganz...
Das Referenzen uebergeben werden ist doch normal und in C heissen die Referenzen Pointer.
Wenn Du wirklich mit Value Typen arbeitest, musst Du immer Boxing vermeiden - d.h. es wird durch den Compiler eine Referenz um Deinen Wert gebaut, was dann manchmal wiederholt passieren kann, so dass Du dann mehrere Referenzen hast.
Ein Objekt hat einen Overhead von 12/36 Bytes - also so viel ist das nicht.

106 Beiträge seit 2011
vor 11 Jahren

Hallo Bazhosh,

du kannst auch Alternativ, falls du das Array nicht übergibst sondern nur die einzelnen ByteWerte, mit "ref" arbeiten.

@weismat:

und in C heissen die Referenzen Pointer

Bist du dir da sicher?
Wenn ich mich nicht täusche dann ist in C++ das Äquivalent zu einer Referenz auch eine Referenz. Ein Pointer ist ein Zeiger auf eine Speicheradresse. Somit würde man bei einem Pointer nicht mit der Referenzierten Variable, sondern mit dem Speicherbereich arbeiten.

MfG
Rabban

W
872 Beiträge seit 2005
vor 11 Jahren

ref in C# und & in C dienen dazu Speicheradressen zu uebergeben und ein Funktion per Referenz zu uebergeben. Das ist wichtig, wenn Du in C# ein Werttyp per Referenz uebergeben wird.
Ein Werttyp (und dazu gehoert das Bytearray) uebergibst Du automatisch per Referenz, ohne dass Du einen ref Operator brauchst.

Hinweis von herbivore vor 11 Jahren

Ein Byte-Array ist wie jedes Array in C# ein Referenztyp. Der letzte Satz muss daher mit "Ein Referenztyp" beginnen. Die Schlussfolgerung, dass dann automatisch eine Referenz übergeben wird, ist denn ja auch richtig.

106 Beiträge seit 2011
vor 11 Jahren

Ein Werttyp (und dazu gehoert das Bytearray) uebergibst Du automatisch per Referenz, ohne dass Du einen ref Operator brauchst.

Widersprichst du dir da nicht selber?
Das ByteArray ist ein Referenztyp, das Byte ein Werttyp. Werttypen werden aber niemals automatisch als Referenz übergeben, das passiert nur wenn man "ref" benutzt.
Und Referenztypen werden, wie der Name schon sagt, immer als Referenz übergeben.

Du kannst auch Alternativ, falls du das Array nicht übergibst sondern nur die einzelnen ByteWerte, mit "ref" arbeiten.

Wenn du dir meinen Satz nochmal genau durchliest, wirst du feststellen, das ich nur gesagt habe, das man einen Werttyp mit "ref" als Referenz übergeben kann.

MfG
Rabban

C
258 Beiträge seit 2011
vor 11 Jahren

Du übergibst an deine Funktion aber einen ByteArray und kein byte deswegen ist es ein Referenztyp.

Die eckigen klammern sagen dem Compiler das es sich um ein Array handelt (der typ der Elemente spielt dabei keine rolle) und ein Array ist nunmal ein Referenztyp.

Und ref übergibt keine speicheradresse. sonder den wert als referenztyp.
um direkt an speicheradressen zu kommen musst du auch in c# mit unsafe und & und * arbeiten

back2Topic arrays werden natürlich nicht bei funktionsübergabe kopiert. Das liegt auch nicht an der dll die du benutzt.
Man stellt sich den Speicherverbrauch auf dem Stack vor wenn du jedes mal 4000 byte draufpackst wenn du eine Funktion mit einem Int Array der länge 1000 aufrufst. Nach 8 verschachtelten functionsaufrufen hättest du einen Stack overflow (bin mir nicht sicher ob der Stack 32kb hat).

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo zusammen,

(bin mir nicht sicher ob der Stack 32kb hat).

der Stack in C# hat standardmäßig eine Größe von einem Megabyte. Das wäre aber trotzdem schnell verbraucht, möglicherweise schon bei einem einzelnen Funktionsaufruf, wenn Arrays Werttypen wären und per call by value übergeben werden würden.

Und ref übergibt keine speicheradresse. sonder den wert als referenztyp.

Darüber kann man unterschiedlicher Ansicht sein. Technisch gesehen wird schon eine Speicheradresse übergeben. Auch von der praktisch Auswirkung stimme ich dir nicht ganz. Technisch gesehen wird durch ref eine Referenz auf die Variable übergeben, wo ohne ref deren Inhalt übergeben werden würde. Praktisch gesehen bedeutet das, dass Änderungen innerhalb der Methode an dem Parameter selbst (z.B. eine Zuweisung an den Parameter) zu einer Änderung des Inhalts der übergebenen Variable führen.

Ob die Variable dabei von ein Werttyp oder von einem Referenztyp ist, spielt keine Rolle. Durch ref wird ermöglicht, den Inhalt der Variable (egal, ob dieser ein Wert oder eine Referenz ist) von innerhalb der aufgerufenen Methode aus zu ändern.

So richtig klar wird einem ref wohl erst dann, wenn man gedanklich klar zwischen Objekten (und Werten) auf der einen Seite und Variablen auf der anderen Seite trennt. Insbesondere Variablen und Objekte sind zwei ganz verschiedene Paar Schuh. Ich denke schon, dass du es verstanden hattest, aber für meine Begriffe kam aus der knappen Beschreibung das tatsächliche Geschehen und Verhalten nicht richtig raus.

Weitere Infos gibt es in [Artikel] Parameter-Übergabemechanismen: call by value vs. call by reference (ref/out).

Wenn ich mich nicht täusche dann ist in C++ das Äquivalent zu einer Referenz auch eine Referenz. Ein Pointer ist ein Zeiger auf eine Speicheradresse.

Weder Pointer in C++ noch Referenzen in C++ sind ein direktes 1:1 Äquivalent von Referenzen in C#.

Referenzen in C++ entsprechend eher ref in C#, nur dass man sie an jeder beliebigen Stelle benutzen kann und nicht nur bei der Parameterübergabe. In C++ ist eine Referenz nach der Initialisierung bzw. besser gesagt Bindung an die referenzierte Variable untrennbar mit dieser verbunden und während der gesamten Lebensdauer der Referenz ist diese ein Synonym für die referenzierte Variable. Jede Änderung und jede Operation auf der Referenz wirkt so, als wäre sie direkt auf der referenzierten Variable ausgeführt worden.

In C# sind Referenzen dagegen (Referenz-)Variablen, deren Inhalt man durch eine einfache Zuweisung ändern kann, sie also durch bzw. nach der Änderung auf ein anderes Objekt referenzieren. Außerdem gibt es (vom Boxing mal abgesehen) Referenzen in C# nur für Referenztypen, aber nicht für Werttypen. Dafür enthalten in C# alle Variablen eines Referenztyps automatisch Referenzen. Die Verwendung von Referenzen für Referenztypen ist in C# also obligatorisch.

Pointer in C/C++ und Pointer in C# entsprechend sich weitestgehend.

Der Unterschied zwischen Pointern (egal ob in C/C++ oder in C#) und Referenzen in C# besteht vor allem darin, dass es für Referenzen keine Pointer-Arithmetik gibt und dass alle Zugriffe (außer der Zuweisung) auf eine Referenz automatisch dereferenzierend erfolgen, also immer automatisch auf das referenzierte Objekt wirken. Man kann also bei einer Referenz in C# (zum Glück) nicht wie bei einem Pointer selbst entscheiden, ob man mit der Adresse selbst oder mit dem Objekt/Wert an der Adresse arbeiten möchte.

herbivore

Suchhilfe: 1000 Worte, Zeiger, Zeigers, zeigen, gezeigt, Pointer, Pointers, Refernz, Refernzen, referenzieren, referenziert, dereferenzieren, dereferenziert, Objekt, Objekts, Objekte, Object, Objects, Werttyp, Werttyps, Werttypen, Wert-Typ, -Typs, -Typen, Referenztyp, Referenztyps, Referenztypen, Referenzsemantik, Wertsemantik, -Semantik, Adresse, Adressen, Speicheradresse, Speicheradressen, c, cpp, csharp, stack, heap, Referenzvariable, Referenzvariablen, Wertvariable, Wertvariablen, Variable, Variablen, Parameter, Parameterübergabe, callbyvalue, call-by-value, callbyreference, call-by-reference

B
Bazhosh Themenstarter:in
9 Beiträge seit 2010
vor 11 Jahren

Danke für die Antworten.

Mein Problem bestand darin, dass ich einen Wrapper für eine C++ Bibliothek benutzt hatte, und dieser immer eine Kopie der Byte-Arrays erstellt hat. Ich dachte erst, dass das automatisch passiert und das hat mich etwas verwirrt.

Ich muss den Wrapper wohl selber schreiben, da der Speicherverbrauch sonst viel zu groß wird...

C
9 Beiträge seit 2012
vor 11 Jahren

Hallo,
ich möchte gerne klarstellen, dass die verbreitete Aussage, dass Referenztypen standarmässig _ByRef _übergeben würden, nicht zutrifft.
In C# werden _alle _Variablen, ob Wertetyp oder Referenztyp, im Standard - also ohne _ref _oder out Modifizierer - _ByValue _übergeben.
Es wird jeweils der _Inhalt _der Variablen kopiert, bei Wertetypen sind das eben die Daten des "Objekts" selbst, bei Referenztypen ist es der Verweis auf die referenzierte Instanz.
Im Ergebnis arbeitet man beim Referenztyp dann natürlich auf derselben Instanz,
deshalb sieht es ähnlich aus, wie eine ByRef-Übergabe. Es gibt aber einen kleinen, aber wesentlichen Unterschied:

Wenn man Referenztypen mit _ref _übergibt, kann man den Verweis selbst ändern, so dass die Variable des aufrufenden Codes auch modifiiziert wird.
Ohne _ref _ geht das nicht, wenn man einem nicht-ref Parameter einen neuen Wert zuweist,
bleibt diese Änderung beim Verlassen der Methode nicht erhalten.
Funktionell entspricht ref bei Referenztypen der Übergabe eine zweifachen Pointers in C++.



string s1 = "initial";
string s2 = "initial";
Foo(s1);
Foo2(ref s2);
=> s1 == initial
=> s2 == geändert

void Foo(string s)
{ 
    s = "geändert";
}
void Foo2(ref string s)
{ 
    s = "geändert";
}


Gruß,
Christoph