Прочитать на английском

Поделиться через


Работа с RESX-файлами программным способом

Примечание

Эта статья относится к платформа .NET Framework. Сведения, применимые к .NET 5+ (включая .NET Core), см. в разделе "Ресурсы" в RESX-файлах.

Поскольку XML-файлы ресурсов (RESX-файлы) должны иметь четко определенный XML-формат (включая заголовок, который должен соответствовать конкретной схеме и за которым следуют данные в парах "имя-значение"), создание этих файлов вручную может приводить к ошибкам. RESX-файлы можно также создавать программным способом, используя типы и члены из библиотеки классов .NET. Кроме того, библиотеку классов .NET можно использовать для извлечения ресурсов, хранящихся в RESX-файлах. В этой статье рассматривается использование типов и членов из пространства имен System.Resources для работы с RESX-файлами.

Здесь рассматривается работа с XML-файлами (RESX-файлами), содержащими ресурсы. Сведения о работе с двоичными файлами ресурсов, внедренными в сборки, см. в статье ResourceManager.

Предупреждение

Существуют также способы работы с RESX-файлами, отличные от программных. При добавлении в проект Visual Studio файла ресурсов среда Visual Studio предоставляет интерфейс для создания и обслуживания RESX-файла и во время компиляции автоматически преобразует RESX-файл в RESOURCES-файл. Для непосредственной работы с RESX-файлом можно также использовать текстовый редактор. Однако следует соблюдать осторожность и избегать изменения содержащихся в файле двоичных данных: это может привести к его повреждению.

Создание RESX-файла

Для создания RESX-файла программным способом можно использовать класс System.Resources.ResXResourceWriter , выполнив следующие действия.

  1. Создайте экземпляр объекта ResXResourceWriter , вызвав метод ResXResourceWriter(String) и указав имя RESX-файла. Имя файла должно включать в себя расширение RESX. Если экземпляр объекта ResXResourceWriter создается в блоке using , явный вызов метода ResXResourceWriter.Close на шаге 3 не требуется.

  2. Вызовите метод ResXResourceWriter.AddResource для каждого ресурса, который необходимо добавить в файл. Используйте перегрузки этого метода для добавления строки, объекта и двоичных данных (массива байтов). Если ресурсом является объект, он должен быть сериализуемым.

  3. Вызовите метод ResXResourceWriter.Close для создания файла ресурсов и освобождения всех ресурсов. Если объект ResXResourceWriter был создан в блоке using , ресурсы записываются в RESX-файл, а ресурсы, используемые объектом ResXResourceWriter , освобождаются в конце блока using .

В полученном RESX-файле имеется соответствующий заголовок и тег data для каждого ресурса, добавленного методом ResXResourceWriter.AddResource .

Предупреждение

Не следует использовать файлы ресурсов для хранения паролей, конфиденциальной информации или личных данных.

В следующем примере создается RESX-файл с именем CarResources.resx, в котором хранятся шесть строк, значок и два объекта, определяемых приложением (два объекта Automobile ). Класс Automobile, определенный и созданный в этом примере, отмечен атрибутом SerializableAttribute.

using System;
using System.Drawing;
using System.Resources;

[Serializable()] public class Automobile
{
   private string carMake;
   private string carModel;
   private int carYear;
   private int carDoors;
   private int carCylinders;

   public Automobile(string make, string model, int year) :
                     this(make, model, year, 0, 0)
   { }

   public Automobile(string make, string model, int year,
                     int doors, int cylinders)
   {
      this.carMake = make;
      this.carModel = model;
      this.carYear = year;
      this.carDoors = doors;
      this.carCylinders = cylinders;
   }

   public string Make {
      get { return this.carMake; }
   }

   public string Model {
      get {return this.carModel; }
   }

   public int Year {
      get { return this.carYear; }
   }

   public int Doors {
      get { return this.carDoors; }
   }

   public int Cylinders {
      get { return this.carCylinders; }
   }
}

public class Example
{
   public static void Main()
   {
      // Instantiate an Automobile object.
      Automobile car1 = new Automobile("Ford", "Model N", 1906, 0, 4);
      Automobile car2 = new Automobile("Ford", "Model T", 1909, 2, 4);
      // Define a resource file named CarResources.resx.
      using (ResXResourceWriter resx = new ResXResourceWriter(@".\CarResources.resx"))
      {
         resx.AddResource("Title", "Classic American Cars");
         resx.AddResource("HeaderString1", "Make");
         resx.AddResource("HeaderString2", "Model");
         resx.AddResource("HeaderString3", "Year");
         resx.AddResource("HeaderString4", "Doors");
         resx.AddResource("HeaderString5", "Cylinders");
         resx.AddResource("Information", SystemIcons.Information);
         resx.AddResource("EarlyAuto1", car1);
         resx.AddResource("EarlyAuto2", car2);
      }
   }
}

Совет

Для создания RESX-файлов можно также использовать Visual Studio. Во время компиляции Visual Studio использует генератор файлов ресурсов (Resgen.exe) для преобразования RESX-файла в двоичный файл ресурсов (RESOURCES-файл) и внедряет этот файл в сборку приложения или вспомогательную сборку.

RESX-файл нельзя внедрить в исполняемый файл или скомпилировать во вспомогательную сборку. Необходимо преобразовать RESX-файл в двоичный файл ресурсов (RESOURCES-файл) с помощью генератора файлов ресурсов (Resgen.exe). Затем полученный RESOURCES-файл можно внедрить в сборку приложения или вспомогательную сборку. Дополнительные сведения см. в разделе Создание файлов ресурсов.

Перечисление ресурсов

В некоторых случаях может потребоваться извлечь из RESX-файла все ресурсы, а не конкретный ресурс. Для этого можно использовать класс System.Resources.ResXResourceReader , предоставляющий перечислитель для всех ресурсов в RESX-файле. Класс System.Resources.ResXResourceReader реализует перечислитель IDictionaryEnumerator, который возвращает объект DictionaryEntry , представляющий конкретный ресурс для каждой итерации цикла. Его свойство DictionaryEntry.Key возвращает ключ ресурса, а свойство DictionaryEntry.Value — значение ресурса.

В следующем примере создается объект ResXResourceReader для файла CarResources.resx, созданного в предыдущем примере, а затем выполняются итерации по файлу ресурсов. В этом примере в объект Automobile добавляются два объекта System.Collections.Generic.List<T> , определенные в файле ресурсов, а в объект SortedList добавляются пять строк из шести. Значения в объекте SortedList преобразуются в массив параметров, который используется для отображения заголовков столбцов в консоли. Значения свойства Automobile также выводятся на консоль.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;

public class Example
{
   public static void Main()
   {
      string resxFile = @".\CarResources.resx";
      List<Automobile> autos = new List<Automobile>();
      SortedList headers = new SortedList();

      using (ResXResourceReader resxReader = new ResXResourceReader(resxFile))
      {
         foreach (DictionaryEntry entry in resxReader) {
            if (((string) entry.Key).StartsWith("EarlyAuto"))
               autos.Add((Automobile) entry.Value);
            else if (((string) entry.Key).StartsWith("Header"))
               headers.Add((string) entry.Key, (string) entry.Value);
         }
      }
      string[] headerColumns = new string[headers.Count];
      headers.GetValueList().CopyTo(headerColumns, 0);
      Console.WriteLine("{0,-8} {1,-10} {2,-4}   {3,-5}   {4,-9}\n",
                        headerColumns);
      foreach (var auto in autos)
         Console.WriteLine("{0,-8} {1,-10} {2,4}   {3,5}   {4,9}",
                           auto.Make, auto.Model, auto.Year,
                           auto.Doors, auto.Cylinders);
   }
}
// The example displays the following output:
//       Make     Model      Year   Doors   Cylinders
//
//       Ford     Model N    1906       0           4
//       Ford     Model T    1909       2           4

Получение определенного ресурса

Помимо перечисления элементов в RESX-файле, можно извлечь конкретный ресурс по имени, используя класс System.Resources.ResXResourceSet . Метод ResourceSet.GetString(String) извлекает значение именованного строкового ресурса. Метод ResourceSet.GetObject(String) извлекает значение именованного объекта или двоичные данные. Этот метод возвращает объект, который затем должен быть приведен (в C#) или преобразован (в Visual Basic) в объект нужного типа.

В следующем примере извлекается строка заголовка и значок формы по именам ресурсов. В нем также извлекаются определяемые приложением объекты Automobile, использованные в предыдущем примере, и эти объекты отображаются в элементе управления DataGridView.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;

public class CarDisplayApp : Form
{
   private const string resxFile = @".\CarResources.resx";
   Automobile[] cars;

   public static void Main()
   {
      CarDisplayApp app = new CarDisplayApp();
      Application.Run(app);
   }

   public CarDisplayApp()
   {
      // Instantiate controls.
      PictureBox pictureBox = new PictureBox();
      pictureBox.Location = new Point(10, 10);
      this.Controls.Add(pictureBox);
      DataGridView grid = new DataGridView();
      grid.Location = new Point(10, 60);
      this.Controls.Add(grid);

      // Get resources from .resx file.
      using (ResXResourceSet resxSet = new ResXResourceSet(resxFile))
      {
         // Retrieve the string resource for the title.
         this.Text = resxSet.GetString("Title");
         // Retrieve the image.
         Icon image = (Icon) resxSet.GetObject("Information", true);
         if (image != null)
            pictureBox.Image = image.ToBitmap();

         // Retrieve Automobile objects.
         List<Automobile> carList = new List<Automobile>();
         string resName = "EarlyAuto";
         Automobile auto;
         int ctr = 1;
         do {
            auto = (Automobile) resxSet.GetObject(resName + ctr.ToString());
            ctr++;
            if (auto != null)
               carList.Add(auto);
         } while (auto != null);
         cars = carList.ToArray();
         grid.DataSource = cars;
      }
   }
}

Преобразование RESX-файлов в двоичные RESOURCES-файлы

Преобразование RESX-файлов во внедряемые двоичные файлы ресурсов (RESOURCES-файлы) имеет значительные преимущества. Хотя RESX-файлы легко читаются и обслуживаются при развертывании приложения, они редко поставляются с готовыми приложениями. Если они распространяются с приложением, то существуют в виде отдельных файлов наряду с исполняемым файлом приложения и сопровождающими его библиотеками. В отличие от RESX-файлов RESOURCES-файлы внедряются в исполняемый файл приложения или сопровождающие его сборки. Кроме того, если локализованные приложения полагаются на RESX-файлы во время выполнения, это означает, что ответственность за обработку перехода к другим ресурсам несет разработчик. Напротив, если создан ряд вспомогательных сборок, содержащих внедренные RESOURCES-файлы, процесс перехода на резервные ресурсы обрабатывается средой CLR.

Для преобразования RESX-файла в RESOURCES-файл используется генератор файлов ресурсов (resgen.exe), который имеет следующий базовый синтаксис:

 resgen.exe .resxFilename

Результат — двоичный файл ресурсов, который имеет такое же корневое имя файла, что и RESX-файл, и расширение RESOURCES-файла. Затем во время компиляции этот файл может быть компилирован в исполняемый файл или библиотеку. Если применяется компилятор Visual Basic, для внедрения RESOURCES-файла в исполняемый файл приложения используйте следующий синтаксис:

vbc filename .vb -resource: .resourcesFilename

При использовании C# синтаксис следующий:

 csc filename .cs -resource: .resourcesFilename

Затем RESOURCES-файл может быть также внедрен во вспомогательную сборку с помощью компоновщика сборок (al.exe), который имеет следующий базовый синтаксис:

al resourcesFilename -out: assemblyFilename

См. также