SettingsAdapterFactory-Klasse

Generiert eine dynamische Implementierung einer Schnittstelle mit Eigenschaften, die an einen SettingsStore gebunden ist und INotifyPropertyChanged implementiert.

Es dauert nur etwa 4 Minuten, um diese Seite zu lesen.

Das Problem

Die Speicherung von Anwendungseinstellungen und der einfache Zugriff darauf sind oft ungeliebte Arbeit. Man will doch nur ein paar wenige Werte für den nächsten Programmstart notieren und sich nicht mit allen Aspekten der verschiedenen Datentypen, Fehlerbehandlung und Programmaktualisierungen beschäftigen. Da liegt es nahe, einfach eine schnell zusammengeschriebene Datenklasse zu serialisieren und irgendwo zu speichern.

Das ist zwar relativ einfach, bringt aber eine ganze Reihe von Problemen mit sich. Man hat keinen Einfluss auf das Speicherformat. .NET serialisiert die Daten so, dass sie gleich danach wieder gelesen werden können. Wenn sich in einer neuen Programmversion aber die Struktur der Einstellungen ändert, lassen sich die vorhandenen Einstellungen im schlimmsten Fall gar nicht mehr lesen. Auch der externe Zugriff auf diese Dateien oder eine Bearbeitung von Hand oder mit einem universellen Konfigurationseditor wie „about:config“ in Firefox ist problematisch. Und die Speicherung der Daten in der Windows-Registry anstelle von Dateien ist kaum noch machbar. Die Fehlersituationen, die sich in der Praxis zeigen, erfordern spezielle Vorkehrungen, damit eine hohe Zuverlässigkeit gewährleistet werden kann.

Sollen die Einstellungen noch dazu in einer interaktiven Programmoberfläche verwendet werden, sollte das System auch über Änderungen der gespeicherten Werte benachrichtigen, so dass sich die Oberfläche auch daran anpassen kann. Oft müssen Programme einfach neugestartet werden, um auch wirklich alle Einstellungen zu übernehmen.

Die Lösung

Diese Factory-Klasse nimmt dem Programmierer diese Komplexität fast vollständig ab und gibt ihm die volle Flexibilität zurück, wenn er sie braucht. Die Definition der Datenstruktur der Anwendungseinstellungen wird dafür auf die kleinstmögliche Form reduziert: Statt Klassen mit ausgeschriebenen Eigenschaften werden nur noch Schnittstellen definiert. Die Eigenschaften in diesen Schnittstellen legen zunächst nur den Namen und Datentyp fest, nicht das Verhalten. Durch Attribute können auch Vorgabewerte angegeben werden. Eigenschaften, die selbst vom Typ einer Schnittstelle sind, dienen der hierarchischen Gliederung der Einstellungen. Hierbei können Schnittstellen für wiederkehrende Strukturen wiederverwendet werden. Für IWindow­State­Settings stehen auch praktische Hilfsmethoden bereit.

Das ganze System besteht aus zwei Teilen, die sich schichtweise ergänzen. Die Settings­Adapter­Factory-Klasse übernimmt dabei die mühselige Arbeit, die kurz und übersichtlich formulierte Schnittstelle auszuimplementieren und mit dem gewünschten Verhalten zu füllen. Für jede Eigenschaft wird dabei ein Getter und Setter erstellt, der den Wert aus einem Speicher-Backend abruft bzw. dorthin übergibt. Die verschiedenen Schnittstellen werden dabei automatisch verknüpft. Änderungsbenachrichtigungen für INotify­Property­Changed werden ebenfalls aus dem Backend durchgereicht. All das passiert zur Laufzeit mittels Reflection und IL-Codegenerierung, so dass keine zusätzlichen Buildschritte oder -werkzeuge nötig sind.

Derzeit sind zwei Backends verfügbar, die die Schnittstelle ISettings­Store implementieren. File­Settings­Store basiert auf der älteren Settings-Klasse, die hier modernisiert und verschlankt wurde und die Einstellungen in einer XML-Datei ablegt. Das Speicherformat ist übrigens kompatibel, so dass die Settings-Klasse mit dem in der Demoanwendung beschriebenen Update-Code durch diese Factory-Lösung ersetzt werden kann. Registry­Settings­Store speichert die Einstellungen in der Registry, unter Nutzung der üblichen Hierarchie aus Unterschlüsseln und Werten. Eigene Backends können z. B. für Client-Server-Architekturen erstellt werden.

Die Anwendung erhält von der Factory eine Implementierung der angegebenen Schnittstelle und greift über diese Schnittstelle darauf zu. Dadurch stehen im Code-Editor Eingabehilfen wie IntelliSense (automatische Vervollständigung und Dokumentation) zur Verfügung und Tipp- oder Typfehler werden bereits vom Compiler erkannt. Daneben ist aber auch der direkte Zugriff auf den Settings­Store über den Schlüsselnamen möglich. Das kann genutzt werden, um alte Schlüssel (Pfadnamen der Eigenschaften) nach Programmänderungen in neue umzubenennen und falls nötig auch die alten Daten zu konvertieren.

Durch diese flexible Aufteilung wird der Datenspeicher auch Plugins zugänglich gemacht. Die bringen dann einfach ihre eigenen Schnittstellen mit und generieren ihre eigene Implementierung mit der Factory, können aber den Settings­Store der Hostanwendung mit einem eigenen Präfix mitbenutzen. Ein Beispiel für diese Nutzung befindet sich ebenfalls in der Demoanwendung.

Kompatibilität: .NET Ab Version 4.0

Beispiel

Das folgende Beispiel zeigt eine Schnittstellendefinition für die Anwendungseinstellungen:

// Defines the application settings.
public interface IAppSettings : ISettings
{
    // Gets or sets the last started version of the application.
    string LastStartedAppVersion { get; set; }

    string[] RecentlyLoadedFiles { get; set; }

    bool IsSoundEnabled { get; set; }

    // Gets the view-related settings.
    IViewSettings View { get; }
}

// Defines view-related application settings.
public interface IViewSettings : ISettings
{
    [DefaultValue(true)]
    bool MonospaceFont { get; set; }

    bool IsWindowOnTop { get; set; }

    // Gets the state settings of the main window. This interface is predefined.
    IWindowStateSettings MainWindowState { get; }
}

Das folgende Beispiel zeigt, wie die Factory und das Settings-Objekt verwendet werden. Die Settings­Helper-Klasse stellt Methoden bereit, die den Umgang mit den Programmeinstellungen erleichtern.

IAppSettings Settings = SettingsAdapterFactory.New<IAppSettings>(
    new FileSettingsStore(
        SettingsHelper.GetAppDataPath(@"Unclassified\SettingsDemo", "SettingsDemo.conf")));

Settings.LastStartedAppVersion = "1.0";

if (Settings.View.RememberLocation)
{
    SettingsHelper.BindWindowState(mainWindow, Settings.MainWindowState);
}

Download

master.zipAktueller Quelltext direkt von GitHub

Es gibt ein öffentliches Git-Repository von SettingsAdapterFactory bei GitHub. Dort sind auch Änderungen am Code dokumentiert.

Lizenz und Nutzungsbedingungen

Vervielfältigung und Weiterverbreitung dieser Datei, verändert oder unverändert, sind gestattet, vorausgesetzt die Urheberrechtsangabe und dieser Hinweis bleiben erhalten. Diese Datei wird wie vorliegend ohne jegliche Garantie oder Gewährleistung angeboten. (GNU All-Permissive-Lizenz)

Statistische Daten

  • Erstellt am 2015-02-13, aktualisiert am 2015-02-18.
  • Ca. 2 100 Codezeilen, geschätzte Ent­wick­lungs­kos­ten: 2 100 - 8 400 €