[C#] Dynamic Config Class

10/01/2014 11:27 Shawak#1
Hello,

i'll release my little config class for people who don't want to use the .net settings.

Code:
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

class Config
{
    // Config starts here

    public static string Username = "Spieler";
    public static int Port = 28282;


    // End of config, don't change anything below
    static BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static;
    static FileInfo configFileInfo = new FileInfo(Constant.ConfigFile);

    public static void Load()
    {
        // Check if file existts
        if (!configFileInfo.Exists)
            return;

        // Deserialize from file
        var json = File.ReadAllText(configFileInfo.FullName, Encoding.ASCII);
        var jObject = JsonConvert.DeserializeObject<Config>(json);

        // Create temp config
        var tmpConf = new Dictionary<string, object>();
        foreach (var field in jObject.GetType().GetFields(bindingFlags))
            tmpConf.Add(field.Name, field.GetValue(null));

        // Load values from temp config
        foreach (var field in typeof(Config).GetFields(bindingFlags))
            field.SetValue(field.Name, tmpConf[field.Name]);
    }

    public static void Save()
    {
        // Create configwith values
        var tmpConf = new Dictionary<string, object>();
        foreach (var v in typeof(Config).GetFields(bindingFlags))
            tmpConf[v.Name] = v.GetValue(null);

        // Serialize to file
        var json = JsonConvert.SerializeObject(tmpConf, Formatting.Indented);
        File.WriteAllText(configFileInfo.FullName, json);
    }
}
You will need the [Only registered and activated users can see links. Click Here To Register...] libary to run it or you will have to write your own (de-)serialization methods.

To load the config simply use:
Code:
Config.Load();
Later in your code you can use for example:

Code:
Console.WriteLine(Config.Username);
Config.Username = "Another Username";
Console.writeLine(Config.Username);
To save your config use:

Code:
Config.Save();
The benefetifs of this class is, that your variables have strong name and type declaration, standard values and you can simply add new variables.

Lg,
Shawak
10/04/2014 06:37 tolio#2
durchaus nett aber warum nicht einfach nen config object machen zb nen Dictionary und das ganze mit nem [Only registered and activated users can see links. Click Here To Register...] speichern und laden

Code:
Dictionary<String, Object> config = new Dictionary<String, Object>();

using(FileStream fs = new FileStream("file.dat", FileMode.Create)){
   BinaryFormatter bf = new BinaryFormatter();
   bf.Serialize(fs, config);
}

using(FileStream fs = new FileStream("file.dat", FileMode.Open)){
   BinaryFormatter bf = new BinaryFormatter();
   config = (Dictionary<String, Object>)bf.Deserialize(fs);
}
dann benötigt das ganze keine extra lib, keine teuren reflection operationen*, einiges weniger an code und das framework erledigt die arbeit

*hab nicht im framework gegraben wie das ganze implementiert ist
10/04/2014 12:40 Mostey#3
Quote:
Originally Posted by tolio View Post
keine teuren reflection operationen
In der Tat, du definierst ja (sofern du das ISerializable interface implementierst) direkt welche Werte unter welchen Keys abgespeichert werden und kannst dabei auch noch den Type festlegen. Da läuft nicht viel mit Reflection.
10/04/2014 15:54 Shawak#4
@tolio: Durchaus, aber leider gehen dir dann die Variablen Typen verloren, außerdem kann es dazu kommen, dass ein Integer als Long geladen wird oder Ähnliches.
10/04/2014 17:37 Mostey#5
Quote:
Originally Posted by Shawak View Post
@tolio: Durchaus, aber leider gehen dir dann die Variablen Typen verloren, außerdem kann es dazu kommen, dass ein Integer als Long geladen wird oder Ähnliches.
Nein.

[Only registered and activated users can see links. Click Here To Register...]

Code:
protected Person(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new System.ArgumentNullException("info");
            name_value = (string)info.GetValue("AltName", typeof(string));
            ID_value = (int)info.GetValue("AltID", typeof(int));
        }
Code:
public virtual void GetObjectData(
        SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new System.ArgumentNullException("info");
            info.AddValue("AltName", "XXX");
            info.AddValue("AltID", 9999);
        }
10/04/2014 19:06 tolio#6
das mit der reflection bezog sich auf den fieldinfo.get/setvalue aufruf, aber wie gesagt da ich nicht gegraben hab was das framework standardmäßig macht will ich mich da auch nicht zu weit aus dem fenster lehnen

@Shawak, nur weil ich das ganze in dem bsp mit dem dict<string, obj> im zweifel zu nem objekt boxe, bedeutet das das nicht das der typ verloren geht.

desweiteren kann man ja auch ein anderes objekt nach wahl nutzen und solange alle im objekt enthaltenen felder ISerializable implementieren; und das ist bei eig allen relevanten basis klassen und typen der fall, also brauch ich auch dann wieder keinen finger zu krümmen außer das ganze in nen formatter rein zu werfen
10/04/2014 19:12 Shawak#7
@tolio: Die extra Lib welche das Json Format zu Verfügung stellt wird heutzutage sowieso in sehr vielen Projekten sowieso gebraucht, ansonsten sollte es auch möglich sein diese durch den JavaScriptSerializer zu ersetzen.

edit: Ist mir durchaus bewusst, allerdings ging es mir nicht casten zu müssen. Außerdem sollen damit ja auch nicht ganze Objekte sondern halt eben nur die statische Konfig Klasse serialisiert werden, dass es in .net serializable gibt sollte jedem klar sein.

@Mostey: Bei dieser Variante muss man wie bei vielen anderen Varianten casten und genau das wollte ich vermeiden, deswegen mein statisches Beispiel. Außerdem wird einem in der IDE nicht angezeigt welche Variablen gegeben sind und die Standartwerte gehen ohne weiteren Aufwand auch verloren. Natürlich ist meine Methode etwas langsamer, wodurch beide ihre Vor- und Nachteile haben. Im Endeffekt muss aber jeder selber entscheiden welche für ihn die geeignetere ist.

Lg
10/05/2014 16:33 -PinkiWinki-#8
Der BinaryFormatter ist in meinen Augen keine brauchbare Lösung. Wieso? Weil er auch Assembly Daten speichert und du entsprechend die Datei nicht mehr laden kannst wenn deine Assembly eine andere Version hat, außerdem finde ich dass Konfigurationsdateien immer in einer lesbaren Text-Form gespeichert werden sollten.
Am besten Xml oder Json, ob man jetzt dafür das .Net Framework oder third-party libraries nutzt ist hier doch latte.

Zu dem Snippet: Ich würde eher einen ConfigManager basteln der die Config-Instanzen managed anstatt die Config Klasse selber statisch zu machen. Damit sparst du dir diese unnötigen Reflection-Methoden.
10/05/2014 19:39 tolio#9
Quote:
Originally Posted by -PinkiWinki- View Post
Der BinaryFormatter ist in meinen Augen keine brauchbare Lösung. Wieso? Weil er auch Assembly Daten speichert und du entsprechend die Datei nicht mehr laden kannst wenn deine Assembly eine andere Version hat
das ist kein problem, geht zb mit:
[Only registered and activated users can see links. Click Here To Register...] +
[Only registered and activated users can see links. Click Here To Register...] Simple
oder
[Only registered and activated users can see links. Click Here To Register...]

Quote:
Originally Posted by -PinkiWinki- View Post
außerdem finde ich dass Konfigurationsdateien immer in einer lesbaren Text-Form gespeichert werden sollten.
sehe ich keinen grund für
10/05/2014 20:31 Shawak#10
Quote:
Originally Posted by tolio View Post

sehe ich keinen grund für
Genau darum soll es in diesem Beispiel aber gehen.
10/12/2014 00:25 MrSm!th#11
Quote:
Originally Posted by -PinkiWinki- View Post
Der BinaryFormatter ist in meinen Augen keine brauchbare Lösung. Wieso? Weil er auch Assembly Daten speichert und du entsprechend die Datei nicht mehr laden kannst wenn deine Assembly eine andere Version hat, außerdem finde ich dass Konfigurationsdateien immer in einer lesbaren Text-Form gespeichert werden sollten.
Am besten Xml oder Json, ob man jetzt dafür das .Net Framework oder third-party libraries nutzt ist hier doch latte.

Zu dem Snippet: Ich würde eher einen ConfigManager basteln der die Config-Instanzen managed anstatt die Config Klasse selber statisch zu machen. Damit sparst du dir diese unnötigen Reflection-Methoden.
+ Defaultwerte sind bei Serialisierung so ne Sache.
Sofern der verwendete Serializer den Konstruktor ignoriert, werden in der Datei nicht vorhandene Settings mit dem Default Wert des Propertys belegt, was in vielen Fällen null ist - worauf ich für meinen Teil nicht bei jedem Setting testen möchte.
Und teils spucken übrigens auch einige Xml Serializer sehr hässliches Xml aus.

Eine eigene Klasse, die sich um das Parsen der Datei und Aufbauen eines Objekts kümmert, hat konsequenterweise den Vorteil, dass man mehr Kontrolle über Dateiformat und Aufbau des Objekts hat.

Der ConfigManager Ansatz hat im Vergleich zur statischen Setting Klasse auch den Vorteil, dass man leicht Unterstützung für verschiedene Profile einbauen kann.

Ich glaube, ich sollte mal meine kleine Settinglib hier posten :x

Quote:
sehe ich keinen grund für
Und dann ist mal ein Fehler im Programm und/oder inkonsistente Settings werden erzeugt und der Nutzer hat Pech gehabt?
Na super.