2012-01-17 4 views
1

J'ai une classe qui doit être un singleton. Il doit également pouvoir charger et enregistrer ses données de champ dans un fichier xml.Modifier un objet avec xml sans créer une nouvelle instance

La méthode suivante renvoie une nouvelle instance, qui casse mon modèle Singleton, laissant des bogues potentiels dans mon code.

public Settings Load() 
{ 
    using (Stream stream = File.OpenRead(FileName)) 
    { 
    XmlSerializer serializer = new XmlSerializer(typeof(Settings)); 
    return (Settings)serializer.Deserialize(stream); 
    } 
} 

Quelle méthode puis-je utiliser pour mettre à jour les données dans mon instance existante, au lieu de renvoyer une instance entièrement nouvelle? J'ai étudié un peu de Linq à Xml, mais je n'en ai trouvé aucun bon exemple. Est-il nécessaire pour moi de conserver toutes mes données de terrain dans un dictionnaire?

+0

DI gestion de vie des conteneurs est généralement un meilleur choix que le Motif Singleton. Voir http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – TrueWill

Répondre

1

je courais dans toutes sortes de bugs qui font un Xml Singleton classe et a fini par mise à la ferraille que je avait des poignées partout. Je l'ai remplacé en utilisant deux façons. Une version en lecture seule qui était pour la lecture des données, et une seconde méthode Using/instruction pour l'écriture des modifications.

Ce en général est le modèle que j'utilise:

public class Settings : IDisposable 
{ 
    string file = "my settings file"; 
    XElement root; 

    private Settings() 
    { 
     root = XElement.Load(file);   
    } 

    private void Dispose() 
    { 
     root.Save(file); 
    } 

    public static Settings Read { get { return new Settings(); } } // return read-only version 

    public static void Write(Action<Settings> handler) 
    { 
     using(Setting settings = new Settings()) 
      handler(settings); 
    } 

    // below here is implentation specific 

    public XElement Root { get { return root; } } 

    public string SettingA 
    { 
     get { return (string)(Root.Attribute("SettingA") ?? (object)string.Empty); } 
     set { Set(Root, "SettingsA", value, true); } 
    } 

    // I wrote this for another StackOverflow thread 
    /// <summary> 
    /// Set any value via its .ToString() method. 
    /// <para>Returns XElement of source or the new XElement if is an ELEMENT</para> 
    /// </summary> 
    /// <param name="isAttribute">true for ATTRIBUTE or false for ELEMENT</param> 
    /// <returns>source or XElement value</returns> 
    private XElement Set(XElement source, string name, object value, bool isAttribute) 
    { 
     string sValue = value.ToString(); 
     XElement eValue = source.Element(name), result = source; 
     XAttribute aValue = source.Attribute(name); 
     if (null != eValue) 
      eValue.ReplaceWith(result = new XElement(name, sValue)); 
     else if (null != aValue) 
      aValue.ReplaceWith(new XAttribute(name, sValue)); 
     else if (isAttribute) 
      source.Add(new XAttribute(name, sValue)); 
     else 
      source.Add(result = new XElement(name, sValue)); 
     return result; 
    } 

    /// <summary> 
    /// Replace with for XAttribute 
    /// </summary> 
    /// <param name="source"></param> 
    /// <param name="value"></param> 
    /// <returns></returns> 
    public static XAttribute ReplaceWith(this XAttribute source, XAttribute value) 
    { 
     XElement parent = source.Parent; 
     if (null == parent) 
      throw new Exception("Source has no parent"); 
     source.Remove(); 
     parent.Add(value); 
     return value; 
    } 

} 

Je n'ai pas utilisé le sérialiseur, donc je ne sais pas si mon modèle s'adaptera pour vous. Je préfère XElement. Donc, pour l'utiliser, vous devrez probablement écrire une classe singleton qui utilise votre classe XmlSerialize non-singleton. Vous n'y auriez accès qu'à travers le singleton.

Mais voici comment je finirais l'utiliser comme est:

string settingA = Settings.Read.SettingA; 

Pour enregistrer une valeur, il serait:

Settings.Write(s => s.SettingA = "new value"); 
0

pourquoi ne pas vous avez quelque chose comme

public Class TheClassHoldingYourObject 
{ 
    private static XmlSerializer _instance; 
    public static Settings Load() 
    { 
     if(_instance != null) return _instance 
     using (Stream stream = File.OpenRead(FileName)) 
     { 
       XmlSerializer serializer = new XmlSerializer(typeof(Settings)); 
       return (Settings)serializer.Deserialize(stream); 
     } 
    } 
} 

Maintenant, vous obtiendrez toujours la même instance

+0

Avoir une classe Container en tant que singleton qui garderait une référence privée à l'objet réel qui m'intéresse semble peu maniable et contraignant. Que puis-je faire si je veux recharger mes "Paramètres"? Dans ce cas, il peut y avoir deux instances de "Paramètres" dans mon code lorsque je recharge depuis xml. –

Questions connexes