| netlib.narod.ru | < Назад | Оглавление | Далее > |
Хотя сегодня .NET Framework может казаться верхом совершенства, существует вероятность (хоть и небольшая), что когда-нибудь грешные разработчики Microsoft будут вынуждены добавить в класс SystemInformation одно – два свойства. Тогда мой класс SysInfoStrings следует обновить, чтобы включить в него эти дополнительные свойства, и все программы этой главы надо будет перекомпилировать с учетом новой версии класса.
А можно ли написать программу, которая автоматически использует все свойства класса SystemInformation, даже те, которых и на свете не было во время ее написания?
Да, можно. Чтобы понять, как это сделать, подумаем, где размещается код класса SystemInformation. Согласно документации на этот класс код хранится в файле System.Windows.Forms.dll. При запуске любой программы из этой главы ОС связывает ее с файлом System.Windows.Forms.dll, и программа может вызывать класс SystemInformation.
Но DLL — это не просто набор кодов. Кроме них, она содержит метаданные в двоичном формате, подробно описывающие классы, включенные в данный файл. Описываются все поля, свойства, методы и события этих классов. Компилятор С# использует эти данные при компиляции программ (вот почему надо устанавливать параметр Reference files). Документация .NET Framework также учитывает эти метаданные.
Так что имеет смысл ввести в программу возможность доступа к этим метаданным во время ее выполнения, динамически обновлять информацию о классах .NET и даже запускать некоторые методы этих классов. Это называется отражением (reflection), и концепция этого процесса заимствована из языка Java. Вообще-то об отражении следует рассказывать в отдельной главе, но оно так здорово подходит для этого приложения, что я не могу удержаться.
В качестве первого шага перепишем класс SysInfoStrings так, чтобы для получения имен свойств и их значений использовалось отражение.
SysInfoReflectionStrings.cs
//---------------------------------------------------------
// SysInfoReflectionStrings.cs (C) 2001 by Charles Petzold
//---------------------------------------------------------
using Microsoft.Win32;
using System;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
class SysInfoReflectionStrings
{
// Поля
static bool bValidInfo = false;
static int iCount;
static string[] astrLabels;
static string[] astrValues;
// Конструктор
static SysInfoReflectionStrings()
{
SystemEvents.UserPreferenceChanged +=
new UserPreferenceChangedEventHandler(UserPreferenceChanged);
SystemEvents.DisplaySettingsChanged +=
new EventHandler(DisplaySettingsChanged);
}
// Свойства
public static string[] Labels
{
get
{
GetSysInfo();
return astrLabels;
}
}
public static string[] Values
{
get
{
GetSysInfo();
return astrValues;
}
}
public static int Count
{
get
{
GetSysInfo();
return iCount;
}
}
// Обработчики событий
static void UserPreferenceChanged(object obj,
UserPreferenceChangedEventArgs ea)
{
bValidInfo = false;
}
static void DisplaySettingsChanged(object obj, EventArgs ea)
{
bValidInfo = false;
}
// Методы
static void GetSysInfo()
{
if(bValidInfo)
return;
// Получаем информацию о свойствах для класса SystemInformation
Type type = typeof(SystemInformation);
PropertyInfo[] apropinfo = type.GetProperties();
// Подсчитываем количество статических доступных для чтения свойств
iCount = 0;
foreach (PropertyInfo pi in apropinfo)
{
if(pi.CanRead && pi.GetGetMethod().IsStatic)
iCount++;
}
// Выделяем память для массивов строк
astrLabels = new string[iCount];
astrValues = new string[iCount];
// Снова перебираем в цикле информацию свойств класса
iCount = 0;
foreach (PropertyInfo pi in apropinfo)
{
if(pi.CanRead && pi.GetGetMethod().IsStatic)
{
// Получаем имя и значение свойства
astrLabels[iCount] = pi.Name;
astrValues[iCount] = pi.GetValue(type, null).ToString();
iCount++;
}
}
Array.Sort(astrLabels, astrValues);
bValidInfo = true;
}
public static float MaxLabelWidth(Graphics grfx, Font font)
{
return MaxWidth(Labels, grfx, font);
}
public static float MaxValueWidth(Graphics grfx, Font font)
{
return MaxWidth(Values, grfx, font);
}
static float MaxWidth(string[] astr, Graphics grfx, Font font)
{
float fMax = 0;
GetSysInfo();
foreach (string str in astr)
fMax = Math.Max(fMax, grfx.MeasureString(str, font).Width);
return fMax;
}
}
Львиную долю работы в этом классе выполняет метод GetSysInfo. Он получает имена свойств и их значения при первом обращении и при каждом их изменении. Оператор typeof языка С# получает тип класса SystemInformation, который сохраняется в переменной типа Туре. Одним из методов Туре является метод GetProperties, возвращающий массив объектов PropertyInfo. Каждый объект в этом массиве является свойством класса SystemInformation. Цикл foreach подсчитывает все свойства, которые одновременно являются статическими и считываемыми. (Знаю, что на данный момент все свойства класса SystemInformation являются статическими и считываемыми, но я пытаюсь сделать программу универсальной.)
Затем программа создает массивы для свойств и их значений и еще раз проходит по массиву PropertyInfo. Свойство Name объекта PropertyInfo содержит имя свойства класса SystemInformation; в нашем случае свойство Name возвращает строки типа «HighContrast» и «IconSize». Метод GetValue получает значение каждого свойства. Статический метод Sort класса Array упорядочивает оба массива (с именами и значениями) по именам свойств.
Функционально программа, использующая класс SysInfoReflectionStrings, является сочетанием SysInfoUpdate и SysInfoEfficient.
SysInfoReflection.cs
//--------------------------------------------------
// SysInfoReflection.cs (C) 2001 by Charles Petzold
//--------------------------------------------------
using Microsoft.Win32;
using System;
using System.Drawing;
using System.Windows.Forms;
class SysInfoReflection: Form
{
protected int iCount;
protected string[] astrLabels;
protected string[] astrValues;
protected float cxCol;
protected int cySpace;
public static void Main()
{
Application.Run(new SysInfoReflection());
}
public SysInfoReflection()
{
Text = "System Information: Reflection";
BackColor = SystemColors.Window;
ForeColor = SystemColors.WindowText;
AutoScroll = true;
SystemEvents.UserPreferenceChanged +=
new UserPreferenceChangedEventHandler(UserPreferenceChanged);
SystemEvents.DisplaySettingsChanged +=
new EventHandler(DisplaySettingsChanged);
UpdateAllInfo();
}
void UserPreferenceChanged(object obj, UserPreferenceChangedEventArgs ea)
{
UpdateAllInfo();
Invalidate();
}
void DisplaySettingsChanged(object obj, EventArgs ea)
{
UpdateAllInfo();
Invalidate();
}
void UpdateAllInfo()
{
iCount = SysInfoReflectionStrings.Count;
astrLabels = SysInfoReflectionStrings.Labels;
astrValues = SysInfoReflectionStrings.Values;
Graphics grfx = CreateGraphics();
SizeF sizef = grfx.MeasureString(" ", Font);
cxCol = sizef.Width +
SysInfoReflectionStrings.MaxLabelWidth(grfx, Font);
cySpace = Font.Height;
AutoScrollMinSize = new Size(
(int) Math.Ceiling(cxCol +
SysInfoReflectionStrings.MaxValueWidth(grfx, Font)),
(int) Math.Ceiling(cySpace * iCount));
grfx.Dispose();
}
protected override void OnPaint(PaintEventArgs pea)
{
Graphics grfx = pea.Graphics;
Brush brush = new SolidBrush(ForeColor);
Point pt = AutoScrollPosition;
int iFirst = (int)((pea.ClipRectangle.Top - pt.Y) / cySpace);
int iLast = (int)((pea.ClipRectangle.Bottom - pt.Y) / cySpace);
iLast = Math.Min(iCount - 1, iLast);
for (int i = iFirst; i <= iLast; i++)
{
grfx.DrawString(astrLabels[i], Font, brush,
pt.X, pt.Y + i * cySpace);
grfx.DrawString(astrValues[i], Font, brush,
pt.X + cxCol, pt.Y + i * cySpace);
}
}
}
Это моя самая последняя версия программы SystemInformation (до главы 6, в которой я добавлю в нее интерфейс для работы с клавиатурой).
| netlib.narod.ru | < Назад | Оглавление | Далее > |