This tip shows a simple way of using a class as a parameter, and processing the class' properties.
Introduction
While working on an application, I got tired of adding lines for every setting I created, so I thought that there had to be better way of processing them all. This is the result.
This piece of code offers to use a class for settings. To add a new setting, one simply has to create a new property in the class. Settings are stored in a simple class, a setting is a property in that class. No extra code is needed to ensure that it gets saved or loaded. The saver will read all properties and save them all to the file. The loader will create the class, and thereby set default settings. Any value present in the file will be applied to the class. At the end, the class is returned for the software to use.
The Code is Simple
It starts with setting up the storage space and the file name for settings. One may change this as one finds necessary.
Next, for saving, all properties are read and saved to file, one by one.
For reading, the file is read, and if the name exists as a property in the settings
class, then it is applied to the class. This also enables to have some validation within the set { }
in the settings
class.
The class used can be named anything, and it is possible to have multiple files and settings.
This example is using a default file name, settings.config, but one can use any file name to create more.
Example
Here is a simple example of a settings
class:
public class Settings
{
public int Setting1 { get; set; }
public bool Setting2 { get; set; }
public double Setting3 { get; set; } = 7.6;
public DateTime Setting4 { get; set; }
public string Setting5 { get; set; } = "test";
public List<string> Setting6 { get; set; } = new List<string>()
{ "test1", "test2" };
}
And how it can be used:
var settings = (Settings)GetSettings("Settings");
settings.Setting1 = Settings.setting1 + 1;
settings.Setting4 = DateTime.Now;
SaveSettings(settings);
Types of Settings
As mentioned before, this will read the properties of the class and apply them. As of now, this has implemented the following types: string
, boolean
, int
, double
, Datetime
and list of strings (List<string>
).
For other types, the code will give an exception, but the code is easily expandable.
Storage Location
The present code will use the local application folder, where it will create a folder with name of the executing (your) application.
Error Handling
This example has basic error handling. One can add additional error handling if needed.
Saving a Class
public void SaveSettings(object settings, string filename = "settings.config")
{
string storageFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Path.GetFileNameWithoutExtension(Application.ExecutablePath));
if (!Directory.Exists(storageFolder))
Directory.CreateDirectory(storageFolder);
if (!Directory.Exists(storageFolder))
throw new Exception($"Could not create folder {storageFolder}");
string settingsFileName = Path.Combine(storageFolder, filename);
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings()
{
Indent = true,
};
XmlWriter writer = XmlWriter.Create(settingsFileName, xmlWriterSettings);
using (writer)
{
writer.WriteStartElement("settings");
var classType = settings.GetType();
var props = classType.GetProperties();
for (int i = 0; i < props.Length; i++)
{
string fileSetting = char.ToLower(props[i].Name[0]) + props[i].Name.Substring(1);
if (props[i].PropertyType == typeof(string))
{
var sett = classType.GetProperty(props[i].Name).GetValue(settings);
if (sett != null)
{
string s = sett.ToString();
if (!string.IsNullOrEmpty(s))
writer.WriteElementString(fileSetting, s);
}
}
else if (props[i].PropertyType == typeof(int))
{
writer.WriteElementString(fileSetting,
classType.GetProperty(props[i].Name).GetValue(settings).ToString());
}
else if (props[i].PropertyType == typeof(double))
{
writer.WriteElementString(fileSetting,
classType.GetProperty(props[i].Name).GetValue(settings).ToString());
}
else if (props[i].PropertyType == typeof(DateTime))
{
var dt = (DateTime)(classType.GetProperty(props[i].Name).GetValue(settings));
writer.WriteElementString(fileSetting, dt.ToOADate().ToString());
}
else if (props[i].PropertyType == typeof(bool))
{
writer.WriteElementString(fileSetting,
classType.GetProperty(props[i].Name).GetValue(settings).ToString());
}
else if (props[i].PropertyType == typeof(List<string>))
{
List<string> values =
classType.GetProperty(props[i].Name).GetValue(settings) as List<string>;
string val = string.Join(",", values.ToArray());
writer.WriteElementString(fileSetting, val);
}
else
throw new Exception($"Unknown setting type found: {props[0].PropertyType}");
}
writer.WriteEndElement();
writer.Flush();
}
}
Loading a Class
Here is the code to read a file and apply properties into the class and return it.
Note that some parts throw an exception, but can be changed to simply returning default settings.
Also note, that the class returns an object, as it is not possible to return the actual class. Therefore, it is needed to typecast the result the class used.
Example:
var mySettings = (MySettingsClass)GetSettings("MySettingsClass");
public object GetSettings(string settingsClassName, string filename = "settings.config")
{
var settingsType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from t in assembly.GetTypes()
where t.Name == settingsClassName
select t).FirstOrDefault();
object settings = Activator.CreateInstance(settingsType);
string storageFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Path.GetFileNameWithoutExtension(Application.ExecutablePath));
if (!Directory.Exists(storageFolder))
Directory.CreateDirectory(storageFolder);
if (!Directory.Exists(storageFolder))
throw new Exception($"Could not create folder {storageFolder}");
string settingsFileName = Path.Combine(storageFolder, filename);
if (!File.Exists(settingsFileName))
{
SaveSettings(settings);
return settings;
}
XmlDocument settingsFile = new XmlDocument();
try
{
settingsFile.Load(settingsFileName);
}
catch (Exception ex)
{
throw ex;
SaveSettings(settings);
return settings;
}
XmlNode settingsNode = null;
int n = 0;
while (n < settingsFile.ChildNodes.Count && settingsNode == null)
{
if (settingsFile.ChildNodes[n].Name.ToLower() == "settings")
settingsNode = settingsFile.ChildNodes[n];
n++;
}
if (settingsNode == null)
{
throw new Exception($"Settings section is not found
in settings file {settingsFileName}");
return settings;
}
var classType = settings.GetType();
var props = classType.GetProperties();
foreach (XmlNode setting in settingsNode.ChildNodes)
{
if (setting.ParentNode.Name.ToLower() != "settings")
break;
if (setting.NodeType != XmlNodeType.Element)
continue;
var settingName = props.Where
(w => string.Compare(w.Name, setting.Name, true) == 0).ToList();
if (settingName.Count == 0)
continue;
if (string.IsNullOrEmpty(settingName[0].Name))
continue;
if (settingName[0].PropertyType == typeof(string))
classType.GetProperty(settingName[0].Name).SetValue(settings, setting.InnerText);
else if (settingName[0].PropertyType == typeof(int))
{
int val = 0;
if (int.TryParse(setting.InnerText, out val))
classType.GetProperty(settingName[0].Name).SetValue(settings, val);
}
else if (settingName[0].PropertyType == typeof(double))
{
double val = 0;
if (double.TryParse(setting.InnerText, out val))
classType.GetProperty(settingName[0].Name).SetValue(settings, val);
}
else if (settingName[0].PropertyType == typeof(DateTime))
{
double val = 0;
if (double.TryParse(setting.InnerText, out val))
classType.GetProperty(settingName[0].Name).SetValue
(settings, DateTime.FromOADate(val));
}
else if (settingName[0].PropertyType == typeof(bool))
{
bool val = (string.Compare("true",
setting.InnerText, true) == 0) || setting.InnerText == "1";
classType.GetProperty(settingName[0].Name).SetValue(settings, val);
}
else if (settingName[0].PropertyType == typeof(List<string>))
{
string val = setting.InnerText.Trim();
if (string.IsNullOrEmpty(val))
{
classType.GetProperty(settingName[0].Name).SetValue
(settings, new List<string>());
continue;
}
List<string> values = val.Split(',').ToList();
classType.GetProperty(settingName[0].Name).SetValue(settings, values);
}
else
throw new Exception
($"Unknown setting type found: {settingName[0].PropertyType}");
}
return settings;
}
Conclusion
This is just a simple way of using a class as a parameter, and processing the class' properties. It has the most common types, but can easily be expanded. Also, the file name and location is easily changed.
Enjoy!
History
- 23rd February, 2022: Initial version
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.