Feedback

C# - Via Reflection Default-Werte initialisieren

Veröffentlicht von am 6/15/2012
(1 Bewertungen)
Sinn und Zweck

Leider hat auch .Net den Unsinn mit der Klasse String nicht beendet, obschon sie sich über weite Teile wie ein 'Wert' verhält. Die Unterscheidung zwischen Leer ("") und Null (null) ist, meiner Meinung nach, gänzlich überflüssig und bedeutet nur Arbeit. (Fast schon peinlich für eine Sprache die eine Garbage-Collection bietet.)

Doch auch bei anderen Elementen zweifelt man am Sinn. So zum Beispiel bei generischen Listen (List<..>). Ein großer Teil des Codes beschäftigt sich damit, und häufige Fehlerursache ist eben eine fehlende Initialsierung.

Funktion
SetPublicDefaults initialiert Strings, List<...> und Objekte die einen parameterlosen Konstruktor besitzen, auch rekursiv.
Die Initialisierung kann erzwungen werden, auch wenn das Objekt (String, List<...>) nicht 'null' ist.

Grenzen
Als externe Funktion können natürlich nur 'public' Felder und Properties bearbeitet werden. Die unter C++ üblich Friend-Deklaration fehlt ja leider.

[Update]
2012-10-07
Bei einer bereits erfolgten teilweisen Initialisierung (zum Beispiel durch andere Programmteile) funktioniert nun auch die (nachträgliche) nicht erzwungene rekursive Initialisierung.

Siehe auch
Public Fields und Properties via Reflection lesen / schreiben

Nutzung

public partial class Vcard {
public String FrontName { get; set; }
public String MiddleName { get; set; }
public String RearName { get; set; }
...
}

public partial class Info {
public Vcard person { get; set; }
...
}

Info info;

info = new Info();
// info.person existiert hier noch nicht

diub.Generic.SetPublicDefaults(vcard, false);
// info.person existiert jetzt
// info.person.FrontName existiert ebenfalls und ist ""
public class xType {
private static bool OkToSet(Object Obj, PropertyInfo pi, bool Force) {
	bool status;

	if (Force) return true;
	status = pi.GetValue(Obj, null) == null;
	return status;
}

private static bool OkToSet(Object Obj, FieldInfo fi, bool Force) {
	bool status;

	if (Force) return true;
	status = fi.GetValue(Obj) == null;
	return status;
}

public static void SetPublicDefaults(Object Obj, bool Force) {
	Type type, sub_type;
	Object sub_obj;
	
	type = Obj.GetType();
	foreach (PropertyInfo pi in type.GetProperties()) {
		sub_type = pi.PropertyType;
		if (sub_type == typeof(System.String)) {
			if (OkToSet(Obj, pi, Force)) pi.SetValue(Obj, "", null);
		} else
            if (diub.Text.Substring(sub_type.ToString(), 0, 31) == "System.Collections.Generic.List") {
                if (OkToSet(Obj, pi, Force)) {
                    sub_obj = Activator.CreateInstance(sub_type);
                    pi.SetValue(Obj, sub_obj, null);
                }
            }
            else {
                if (sub_type.BaseType == typeof(System.Object)) {
                    if (OkToSet(Obj, pi, Force)) {
                        try {
                            sub_obj = Activator.CreateInstance(sub_type);
                            SetPublicDefaults(sub_obj, Force);
                            pi.SetValue(Obj, sub_obj, null);
                        } catch (Exception) {
                        }
                    }
                    else {
                        try {
                            sub_obj = pi.GetValue(Obj, null);
                            SetPublicDefaults(sub_obj, Force);
                        } catch (Exception) {
                        }
                    }
                }
            }
	}

	foreach (FieldInfo fi in type.GetFields()) {
		sub_type = fi.FieldType;
		if (sub_type == typeof(System.String)) {
			if (OkToSet(Obj, fi, Force)) fi.SetValue(Obj, "");
		} else
            if (diub.Text.Substring(sub_type.ToString(), 0, 31) == "System.Collections.Generic.List") {
                if (OkToSet(Obj, fi, Force)) {
                    sub_obj = Activator.CreateInstance(sub_type);
                    fi.SetValue(Obj, sub_obj);
                }
            }
            else {
                if (sub_type.BaseType == typeof(System.Object)) {
                    if (OkToSet(Obj, fi, Force)) {
                        try {
                            sub_obj = Activator.CreateInstance(sub_type);
                            SetPublicDefaults(sub_obj, Force);
                            fi.SetValue(Obj, sub_obj);
                        } catch (Exception) {
                        }
                    }
                    else {
                        try {
                            sub_obj = fi.GetValue(Obj);
                            SetPublicDefaults(sub_obj, Force);
                        } catch (Exception) {
                        }
                    }
                }
            }
	}
}

}

2 Kommentare zum Snippet

System.ArgumentException schrieb am 11/7/2015:
"Die Unterscheidung zwischen Leer ("") und Null (null) ist, meiner Meinung nach, gänzlich überflüssig und bedeutet nur Arbeit."

Wie kommst du darauf? Ein Empty String ist ein Wert. Der macht Sinn. Null ist nicht zugewiesen.
Empty Strings kommen in Produktiv Code öfter vor als du denkst...
diub schrieb am 11/8/2015:
@System.ArgumentException: So ganz verstehe ich deine Frage nicht. Ich versuche mich anzunähern.

Einen Wert wie Leer ("") und Null zu unterscheiden, finde ich nicht nur bei Strings recht dümmlich, aber C# (und eigentlich alle anderen Sprachen, die ich so kenne) initialisieren Objekt-Variablen grundsätzlich nicht (ein 'default(String)' ergibt tatsächlich Null). Mir wäre es anders lieber. Drum halt auch das Snippet.

Wobei mir von alten Datenbank anwendungen bekannt ist, das ein Feld mit Null tatsächlich als 'nicht Initialsiert' eine andere Bedeutung für die Programmlogik hat, als Leer.

Strings bekommen also ein Leer ("") zugewiesen, falls Sie Null sind.

Im übrigen muss man ja (sonst) ständig Leer und Null unterscheiden, sonst schägt ja schon die Abfrage mit 'Length' fehl. Woruf ich keine Lust habe, weil es für die Logik meiner Programme sinnlos ist.

Die Frage ist aber doch: warum bei String nicht und bei einer eigenen / anderen Klasse doch? Das ganze läuft schließlich rekursiv über alle Objekte. So gesehen ist das nur eine Gleichbehandlung.

 

Logge dich ein, um hier zu kommentieren!