This article has been localized into Dutch by the community.
Een instellings- (settings) class gebaseerd op Reflection
Ik dacht dat ik dit deel van het tutorial over Reflection maar eens moest beëindigen met een fraai en nuttig voorbeeld. Het is wat langer dan de gebruikelijke voorbeelden op deze site, maar ik hoop dat je het echt nuttig vindt. Het gebruikt een heleboel van het spul waar we in de laatste paar hoofdstukken naar hebben gekeken , dus ik hoop dat je het kunt bijhouden.
Een gebruikelijk scenario bij het creëren van wat voor applicatie dan ook, is de wens om de instellingen van de gebruikers op te slaan. Wanneer je diverse instellingen hebt, zul je waarschijnlijk een Settings class creëren die het laden en opslaan van de gewenste instellingen voor zijn rekening neemt. Elke keer als je een nieuwe instelling nodig hebt in je Settings class, moet je de Load() en Save() methods bijwerken om de nieuwe instelling vast te leggen. Maar waarom zouden we de Settings class niet zelf zijn eigen properties laten ontdekken en die automatisch laten laden en opslaan? Met Reflection is dat heel gemakkelijk, en als je de andere hoofdstukken van de Reflection sectie van dit tutorial hebt gelezen, zul je het volgende voorbeeld kunnen begrijpen.
Om het beter te laten passen in een klein voorbeeld, sla ik informatie over een persoon op in plaats van de settings van de applicatie, maar ik hoop dat je het algemene idee erachter begrijpt. Weet wel dat gebruik van Reflection trager gaat dan properties handmatig lezen en schrijven. Dus je moet goed overwegen wanneer je het gebruikt en wanneer je beter een snellere optie kunt gebruiken. In ons voorbeeld gebruiken we een simpel tekstbestand om nog simpeler waarden op te slaan, slechts gescheiden door een | (pipe character). Als je dit gebruikt voor spul in de echte wereld, zal je waarschijnlijk een beter format willen voor je gegevens, misschien XML. En natuurlijk is er weinig foutenbehandeling, dus zou je mogelijk zelf ook het één en ander kunnen toevoegen.
Okee, laten we beginnen. Eerst onze Person class, die je in ook kunt hernoemen als Settings of zoiets om het voor jou wat bruikbaarder te maken:
public class Person
{
private int age = -1;
private string name = String.Empty;
public void Load()
{
if(File.Exists("settings.dat"))
{
Type type = this.GetType();
string propertyName, value;
string[] temp;
char[] splitChars = new char[] { '|' };
PropertyInfo propertyInfo;
string[] settings = File.ReadAllLines("settings.dat");
foreach(string s in settings)
{
temp = s.Split(splitChars);
if(temp.Length == 2)
{
propertyName = temp[0];
value = temp[1];
propertyInfo = type.GetProperty(propertyName);
if(propertyInfo != null)
this.SetProperty(propertyInfo, value);
}
}
}
}
public void Save()
{
Type type = this.GetType();
PropertyInfo[] properties = type.GetProperties();
TextWriter tw = new StreamWriter("settings.dat");
foreach(PropertyInfo propertyInfo in properties)
{
tw.WriteLine(propertyInfo.Name + "|" + propertyInfo.GetValue(this, null));
}
tw.Close();
}
public void SetProperty(PropertyInfo propertyInfo, object value)
{
switch(propertyInfo.PropertyType.Name)
{
case "Int32":
propertyInfo.SetValue(this, Convert.ToInt32(value), null);
break;
case "String":
propertyInfo.SetValue(this, value.ToString(), null);
break;
}
}
public int Age
{
get { return age; }
set { age = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
}
Ik weet het, het is veel. Maar ik zal je door de hele class heen helpen. Om te beginnen, hebben we een aantal 'private fields' die informatie over onze persoon omvatten. Onderin de class hebben we de corresponderende 'public properties' die uiteraard de private fields gebruiken.
We hebben ook een Load() method. Deze zoekt naar het bestand 'settings.dat', en als dat bestaat, leest hij het hele bestand en plaatst elke regel ervan in een array van strings. Vervolgens lopen we door elke setting regel et splitsen hem op in twee delen: Een property naam en een waarde deel. Als beide aanwezig zijn, gebruiken we simpelweg het Type object om de property met de property naam te krijgen, en dan voeren we de waarde ervan in, met gebruik van onze SetProperty method.
De SetProperty() method zoekt naar het type van de te veranderen property, en doet dat dan ook. Tot nu toe ondersteunt het alleen integers en strings, maar, zoals je je waarschijnlijk al realiseert, zou het uitbreiden van deze ondersteuning heel gemakkelijk zijn.
De Save() method geeft een array van PropertyInfo exemplaren, één voor elk van de gedefinieerde properties in de Person class, en gebruikt dan TextWriter om elke property met zijn waarde naar het data bestand te schrijven.
Nu hoeven we alleen nog maar wat code om deze class te gebruiken. Deze kleine applicatie zal proberen de persoon uit het settings bestand te laden, en als dat niet lukt, wordt de gebruiker aangespoord om de informatie te leveren.
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Load();
if((person.Age > 0) && (person.Name != String.Empty))
{
Console.WriteLine("Hi " + person.Name + " - you are " + person.Age + " years old!");
}
else
{
Console.WriteLine("I don't seem to know much about you. Please enter the following information:");
Type type = typeof(Person);
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo propertyInfo in properties)
{
Console.WriteLine(propertyInfo.Name + ":");
person.SetProperty(propertyInfo, Console.ReadLine());
}
person.Save();
Console.WriteLine("Thank you! I have saved your information for next time.");
}
Console.ReadKey();
}
}
Alles hier is behoorlijk triviaal, behalve voor het deel waar we de gebruiker om informatie vragen. En alweer gebruiken we Reflection om alle public properties uit de Person class te krijgen, en dan naar elke ervan te vragen.
Als oefening voor de lezer stel ik voor dat je de Person class uitbreidt om er meer informatie in te kunnen stoppen. Voeg gewoon meer properties toe, en je zult zien dat deze info matie eveneens opgeslagen en geladen wordt.