Hallo zusammen,
wie kann ich aus einem Defaultkonstruktor einen überladenen Konstruktor aufrufen, wenn meine Klasse generisch ist?
class Foo<T>
{
Foo(string name) : this(name, ???)
{}
Foo(string name, ref T obj){}
}
Und zwar sollte beim Aufruf obj = null sein, wenn das möglich ist.
Was ist denn T bzw. was solls werden?
Wenns nen einfaches Objekt ist wirds doch sowieso by reference übergeben -> ref-Einschränkung also unnötig.
Siehe Verdeutlichung in meinem nächsten Post.
Aber wenns so bleiben soll: nein, so direkt nicht möglich.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Was ist denn T bzw. was solls werden?
Wenns nen einfaches Objekt ist wirds doch sowieso by reference übergeben -> ref-Einschränkung also unnötig.
Wäre mir neu. Standardmäßig wird alles by Value übergeben, d.h. bei "einfachen" Objekten wird eine Kopie der Referenz übergeben.
Ja, prinzipiell wird alles bei C# call by value
übergeben; hab mich evtl. undeutlich ausgedrückt.
Trotzdem wird bei einer Objekt-Übergabe (=>Referenztyp) die Referenz übergeben => reference will be passed by value.
Siehe dazu auch Referenztyp mit ref Parameter übergeben? Sinnlos? [==> es gibt sinnvolle Fälle] und [Artikel] Parameter-Übergabemechanismen: call by value vs. call by reference (ref/out)
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Die Objekte werden hier als Referenz übergeben, da das Originalobjekt verändert wird.
Ja, deswegen die Frage: wieso willst Du ref
nutzen?
Brauchst Du das wegen eines der in meinem verlinkten Thread vorkommenden Beispiele _für _ref
?
Ansonsten kannst es ja sparen...
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Das war die Antwort auf deine Frage...ich muss das Objekt als ref übergeben, da das Original verändert wird. Anders wird beim Ändern eine Referenzkopie angelegt.
Es wird ja grundsätzlich immer der Zeiger übergeben und keine Kopie des Objektes. Aber beim ändern des Objektes muss doch eine Kopie angelegt werden oder täusche ich mich?
Ich glaub Du hast es nicht verstanden, wie das mit Referenztypen funktioniert, oder?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
public class Person
{
public Person(String name)
{
this.Name = name;
}
public String Name { get; set; }
}
public static class PersonHelpers
{
public static void SetName(Person p, string neuerName)
{
p.Name = neuerName;
}
public static void SetName(ref Person p, string neuerName)
{
p.Name = neuerName;
}
public static void SetName(string name, string neuerName)
{
name = neuerName;
}
public static void SetName(ref string name, string neuerName)
{
name = neuerName;
}
}
static void Main(string[] args)
{
Console.WriteLine("BSP1: " + Environment.NewLine);
// Referenztyp
var p = new Person("Peter");
Console.WriteLine("Name: " + p.Name);
// Name Ändern über Methode ohne Ref:
PersonHelpers.SetName(p, "Harald");
Console.WriteLine("Name: " + p.Name);
// Name Ändern über Methode mit Ref:
PersonHelpers.SetName(ref p, "Claudia");
Console.WriteLine("Name: " + p.Name);
Console.WriteLine(Environment.NewLine + Environment.NewLine + "BSP2: " + Environment.NewLine);
// String
string name = "Peter";
Console.WriteLine("Name: " + name);
// Name Ändern über Methode mit Ref:
PersonHelpers.SetName(name, "Harald");
Console.WriteLine("Name: " + name);
// Name Ändern über Methode ohne Ref:
PersonHelpers.SetName(ref name, "Claudia");
Console.WriteLine("Name: " + name);
Console.ReadKey();
}
}
}
Ausgabe:
`BSP1:
Name: Peter
Name: Harald
Name: Claudia
BSP2:
Name: Peter
Name: Peter
Name: Claudia`
Fazit:
ref beim Objekt nicht nötig, aber u.a. beim String sehr wohl.
Fällt Dir was auf?
Wenn nicht: nochmal ein Blick in die Links, die ich oben genannt habe.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hat jetzt nichts direkt hiermit was zutun aber indirekt schon 😃
Eventuell liegt es am Typ byte[] beim Aufruf der Methode SerializeObjectToBuffer<byte[]>(ref obj, objSize, out buffer); muss obj als ref übergeben werden, ansonsten wird der Speicher nicht aufgeräumt nach dem die Methode verlassen wird.
class Program
{
static void Main(string[] args)
{
byte[] obj = new byte[262144000];
//Mem: ~250MB
new Random().NextBytes((byte[])obj);
byte[] buffer;
SerializeObjectToBuffer<byte[]>(ref obj, objSize, out buffer);
//Mem: ~250MB
object objout;
Deserialize<object>(ref buffer, out objout);
GC.Collect();
GC.WaitForPendingFinalizers();
//Mem: ~250MB
}
private static void SerializeObjectToBuffer<T>(ref T obj, long objSize, out byte[] buffer)
{
using (MemoryStream memStream = new MemoryStream((int)objSize))
{
new BinaryFormatter().Serialize(memStream, obj);
//Mem: ~500MB
obj = default(T);
GC.Collect();
GC.WaitForPendingFinalizers();
//Mem: ~250MB
buffer = memStream.GetBuffer();
memStream.Close();
}
}
private static void Deserialize<T>(ref byte[] buffer, out T obj)
{
using (MemoryStream mem = new MemoryStream(buffer, false))
{
obj = (T)new BinaryFormatter().Deserialize(mem);
//Mem: ~500MB
mem.Close();
mem.Dispose();
}
buffer = null;
}
}
Darf man fragen, was das werden soll? Was hast Du konkret mit diesem Code vor?
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hab den Code um ein paar Kommentare ergänzt (aktueller Prozessspeicher). Relevant war für mich zu testen bzw. herauszufinden, dass beim Ser/Deserialisieren von Objekten der Speicher nicht
unnötig aufgebläht wird. Das ist natürlich nur eine Demo.
Und warum serialisierst Du nicht einfach direkt in das Objekt, statt ein ByteArray zu verwenden (wie es auch normal wäre)?
public T DeserializeToObject<T>(byte[] bytes)
{
using (var memStream = new MemoryStream(bytes))
{
var bFormatter = new BinaryFormatter();
{
var obj = (T)bFormatter.Deserialize(memStream);
return obj;
}
}
}
Dass beim Erstellen von Objekten Speicher allokiert wird, ist ja völlig normal und okay. Klar, dass der steigt je mehr Objekte Du erstellst.
Daher nicht alle bytes auf einmal einlesen, sondern sequentiell lesen. Aktuell allokierst Du sofort 250 MB.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ziel war jedes Object (hier obj(byte[])) in einem Buffer (byte[]) und umgekehrt zu serialisieren.
Foo obj;
byte[] objBuffer = GetBuffer(obj);
..
..
mach irgendwas anderes...
..
..
Foo obj = GetObject(objBuffer);
wobei objBuffer zwischengespeichert wird
Wie gesagt, es soll nur die Größe jedes Objektes demonstrieren. Hier sind es 250MB. Kann auch ein X-beliebiges Objekt sein..
Tut mir leid. Ich versteh den Sinn des Vorhabens nicht (und auch kein Zwischenziel) dadurch kann ich Dir keinen besseren Vorschlag machen.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Eigentlich wollte ich damit nur zeigen, weshalb ich das Objekt als "ref" übergebe.. 😃
Ja, aber evtl. kann mein Dein ganzes Vorhaben verbessern, sodass Du ref gar nicht brauchst ((was ich eben vermute)
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Mein ganzes Vorhaben zu beschreiben wäre etwas zu komplex, deshalb nur eine kleine Demonstration. Ziel ist es Objekte effizient zu serialisieren und vor allem sie Zeitnah aus dem Speicher zu entladen.
Klingt ziemlich nach (premature optimization is the root of all evil
Weil effizient ist das, was Du da machst nicht. Und aus dem Speicher nimmt der GC normal serialisierte Objekte ja auch erst, wenn wirklich keine Referenz mehr auf dem Objekt besteht.
Sehe also keinerlei Vorteil in Deiner Variante; nur die gesteigerte Komplexität
Wenn Du eine Lösung hast, die wirklich effizienter sein sollte, so poste sie bitte hier, damit die Nachwelt was davon hat.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Bezogen auf die Eingangsfrage:
Rein von der Syntax geht das mithilfe eines kleines Tricks:
class Foo<T>
{
Foo(string name)
: this(name, ref new Helper<T>().obj)
{ }
Foo(string name, ref T obj) { }
}
class Helper<T>
{
public T obj;
}
Mal so nebenbei erwähnt:
Ich halte es für unpassend, im Konstruktor ref zu nutzen.
Ob das in diesem konkreten Fall nun sinnvoll ist, oder nicht, sei mal dahin gestellt, aber wenn ich einen Konstruktor verwende, erwarte ich nicht, dass meine Referenz, die ich übergebe, auf einmal komplett überschrieben wird und auf ganz ein anderes Objekt verweist.
Ich weiß nicht, ob das jetzt so verständlich rüber kam, aber ein Konstruktor hat in meinen Augen nur die Aufgabe, das Objekt für die Verwendung entsprechend und grundlegend vorzubereiten. Also z.B. eine private Liste zu initialisieren, damit sie genutzt werden und das Objekt seinen Dienst erfüllen kann.
Alles, was darüber hinaus geht, gehört in meinen Augen nicht mehr in einen Konstruktor, sondern in eine separate Methode. Sollte eine Klasse Daten aus einer Daten-Quelle lesen müssen/sollen, so landet das bei mir auch nicht im Konstruktor, sondern in einer separaten Methode oder gar in einer anderen Klasse, die dann das Daten-Objekt befüllen kann.
Außerdem versuche ich ein Objekt, das ich als Parameter bekomme, nur dann zu ändern, wenn diese Änderung auch wirklich im Anforderungsbereich der Methode liegt. Die Methode soll nur ihre Aufgabe erfüllen und nichts darüber hinaus. Sie soll also auch die Parameter in Ruhe lassen, außer es ist zwingend erforderlich oder gewünscht, dass ein Parameter als Referenz behandelt und auch abgeändert wird, aber das soll dann entweder aus dem Namen zu erkennen oder in der Dokumentation nachzulesen sein.
Das Gleiche denke ich über out. Das "Ergebnis" von einem Konstruktor ist das initialisierte Objekt und nichts weiter. Da gehört auch keine Berechnung hinein, weshalb ein zusätzliches Ergebnis, das über out abzurufen wäre, gar nicht in Frage kommen sollte.
Vielleicht sehe ich das etwas streng oder gar falsch, aber das ist meine Meinung und von daher tut mir allein der Anblick von ref im Konstruktor schon in den Augen weh.
Selbst wenn es in diesem Fall sinnvoll ist, dann suche ich lieber nach einem alternativen Weg, wie z.B. eine Methode "Initialice", oder besser ein spezifisch angepasster Name, der dann halt noch eine weitere Aufgabe hat, die nicht im Konstruktor steht.
Diese Denkweise ist aus dem Single responsibility principle entstanden, das ich für mich auch auf Methoden übertragen habe. Jede Methode übernimmt exakt eine Aufgabe, nicht mehr, nicht weniger.
Sind es mehr Aufgaben, teile ich die Methode wenn Möglich auf.
... aber wenn ich einen Konstruktor verwende, erwarte ich nicht, dass meine Referenz, die ich übergebe, auf einmal komplett überschrieben wird und auf ganz ein anderes Objekt verweist.
Egal ob normale Methode oder Konstruktor: Wo immer ref bei der Signatur auftaucht, muss man es auch beim Aufruf angeben, sonst gibt es einen Syntaxfehler. Man kann also nicht wirklich davon überrascht werden. Das ändert allerdings nichts an der restlichen Argumentationskette.