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< Назад | Оглавление | Далее >

Сайт управляется системой uCoz