This article has been localized into Russian by the community.
Отражение класса настроек
Итак, я решил закончить эту часть урока об отражении классным и полезным примером. Он немного больше, чем обычные примеры здесь на сайте, но, надеюсь, вы найдете его действительно полезным. Он использует кучу вещей, которые мы изучили в течение последних пары глав, так что вы можете идти в ногу.
Распространенным сценарием при создании любого приложения является желание сохранить настройки пользователей. Когда вы получите несколько настроек, вы, вероятно, создадите класс настроек, который будет обрабатывать загрузку и сохранение необходимых настроек. Каждый раз, когда вам понадобится новая настройка в классе настроек, вам придется обновлять методы Load() и Save (), чтобы включить эту новую настройку. Но Эй, почему бы не позволить классу Settings обнаружить свои собственные свойства, а затем загрузить и сохранить их автоматически? С Reflection это довольно легко, и если Вы прочитали другие главы в разделе Reflection этого учебника, вы должны быть в состоянии понять следующий пример.
Чтобы лучше вписать всё это в небольшой пример, я буду сохранять информацию о человеке вместо настроек приложения. Надеюсь, вы все равно получите общее представление. Имейте в виду — алгоритм с использованием Отражения работает МЕДЛЕННЕЕ, чем чтение и запись известных свойств вручную — это следует учитывать, когда вам требуется наиболее быстрый подход. Кроме того, в нашем примере мы используем обычный текстовый файл для хранения наших простых значений, разделенных только символом '|' . Если вы собираетесь использовать это в реальных проектах — вам, вероятно, понадобится лучший формат для ваших данных (возможно XML). Само собой, в данном примере так-же не хватает обработчиков ошибок, поэтому вам вероятно следует добавить парочку.
Хорошо, давайте начнем. Во-первых, наш класс Person, который Вы можете просто переименовать в Settings или что-то в этом роде, чтобы сделать его более полезным для вас:
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; }
}
}
Ладно, я знаю, здесь много всего интересного. Но я помогу тебе пройти через весь класс. Прежде всего, у нас есть несколько личных полей для хранения информации о нашей персоне. В нижней части класса у нас есть соответствующие открытые свойства, которые, конечно же, используют личные поля.
У нас также есть метод load (). Он ищет настройки файла.dat, и если он существует, он считывает весь файл и помещает каждую его строку в массив строк. Теперь мы проходим через каждую строку настроек и разделяем ее на две части: имя свойства и часть значения. Если оба присутствуют, мы просто используем объект Type, чтобы получить свойство с именем свойства, а затем мы устанавливаем значение для него, используя наш собственный метод SetProperty.
Метод SetProperty () смотрит на тип свойства, которое должно быть изменено, и затем действует соответственно. Сейчас он поддерживает только целые числа и строки, но, как вы, вероятно, понимаете, расширить эту поддержку будет довольно легко.
Метод Save () получает массив экземпляров PropertyInfo, по одному для каждого из определенных свойств класса Person, а затем использует TextWriter для записи каждого свойства и его значения в файл данных.
Теперь нам просто нужен код для использования этого класса. Это небольшое приложение попытается загрузить индивида из файла настроек, и если это не удастся, пользователю будет предложено ввести информацию:
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();
}
}
Все здесь довольно тривиально, за исключением той части, где мы запрашиваем у пользователя информацию. Еще раз, мы используем отражение, чтобы получить все публичные свойства класса Person, а затем запрашиваем каждое из них.
В качестве упражнения для чтения я предлагаю вам расширить класс Person, чтобы включить больше информации. Просто добавьте в него дополнительные свойства, и вы увидите, что эта информация также сохраняется и загружается.