Im Internet existieren zahlreiche Diskussionen warum diese Zeilen Code nicht so funktionieren, wie man es instinktiv erwartet:
public class CA {
public String t1;
}
void MethodExampleA (ref Object obj) {
... // Bearbeitung des Objektes obj
}
void MethodExampleB (Object obj) {
... // Bearbeitung des Objektes obj
}
CA ca = new CA();
ca.t1 = "1";
MethodExampleA (ref ca); // nicht möglich
MethodExampleB (ca); // vergebens
// Edit: 2013-01-29 -- Danke an Scavanger, vergass ich zu erwähnen
<b>Da der Typ zur Compile-Zeit nicht bekannt</b> ist funktioniert ein (obj as CA).t1 = "Hallo"; leider nicht.
Hintergrund: Die aufgerufene Funktion liegt in einer DLL die von mehreren Programmen mit verschiedenen Datenstrukturen genutzt wird.
Eine Alternative ist natürlich eine generische Funktion ala MethodExampleC T (T obj). Ein obj.t1 macht trotzdem keinen Sinn, da zur Compile-Zeit <b>der DLL</b> ja nicht bekannt.
// Edit
Gemein ist: innerhalb der Funktion 'MethodExampleB' funktioniert alles. Nur nach dem Aufruf ist alles beim Alten geblieben. Wieso?
Was man (und ich anfangs auch) gerne vergisst: 'Object' ist kein Container für andere Datentypen, sondern selbst ein Datentyp. Daher wird beim Aufruf der Funktion der übergebene Type 'CA' quasi nach 'Object' konvertiert. Deshalb funktioniert der Code überhaupt ohne Fehlermeldung.
Diese Konvertierung ist - natürlich - temporär und verfällt bei Rückkehr aus der Funktion. Und damit auch die Änderungen am 'Object obj' - und eben nicht an 'CA ca'.
Hin und wieder wäre es aber schön, wenn es doch ginge.
Nicht ganz das selbe, aber in vielen Fällen ausreichend ist ein 'Value-Mapping'. Also das 'mappen' der tempörären Änderungen an 'Object obj' auf Werte- oder Referenz-Typ-Ebene
innerhalb von 'CA ca'. Für 'public' Fields und 'Propertys erreicht man das so.
void MapPublicValues (Object Source, Object Target) {
Type type;
Object sub_source;
if (Source == null) return; // macht wohl keine Sinn
if (Target == null) return; // das auch nicht
type = Source.GetType ();
foreach (PropertyInfo pi in type.GetProperties ()) {
sub_source = pi.GetValue (Source, null);
pi.SetValue (Target, sub_source, null);
}
foreach (FieldInfo fi in type.GetFields ()) {
sub_source = fi.GetValue (Source);
fi.SetValue (Target, sub_source);
}
}
// Interessant: es wird keine Referenz dazu benötigt.
void MethodExample (Object obj) {
Object tmp;
tmp = (Object) Activator.CreateInstance(obj.GetType());
.... // tmp bearbeiten
// obj = tmp; : sinnlos, wird Rückkehr verworfen
MapPublicValues (tmp, obj); // Änderungen nach obj 'mappen'
}
3 Kommentare zum Snippet