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


Работа с 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-файла

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

  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);
      }
   }
}
Imports System.Drawing
Imports System.Resources

<Serializable()> Public Class Automobile
    Private carMake As String
    Private carModel As String
    Private carYear As Integer
    Private carDoors AS Integer
    Private carCylinders As Integer

    Public Sub New(make As String, model As String, year As Integer)
        Me.New(make, model, year, 0, 0)
    End Sub

    Public Sub New(make As String, model As String, year As Integer,
                   doors As Integer, cylinders As Integer)
        Me.carMake = make
        Me.carModel = model
        Me.carYear = year
        Me.carDoors = doors
        Me.carCylinders = cylinders
    End Sub

    Public ReadOnly Property Make As String
        Get
            Return Me.carMake
        End Get
    End Property

    Public ReadOnly Property Model As String
        Get
            Return Me.carModel
        End Get
    End Property

    Public ReadOnly Property Year As Integer
        Get
            Return Me.carYear
        End Get
    End Property

    Public ReadOnly Property Doors As Integer
        Get
            Return Me.carDoors
        End Get
    End Property

    Public ReadOnly Property Cylinders As Integer
        Get
            Return Me.carCylinders
        End Get
    End Property
End Class

Module Example
    Public Sub Main()
        ' Instantiate an Automobile object.
        Dim car1 As New Automobile("Ford", "Model N", 1906, 0, 4)
        Dim car2 As New Automobile("Ford", "Model T", 1909, 2, 4)
        ' Define a resource file named CarResources.resx.
        Using resx As 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)
        End Using
    End Sub
End Module

Подсказка

Вы также можете использовать Visual Studio для создания RESX-файлов. Во время компиляции 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
Imports System.Collections
Imports System.Collections.Generic
Imports System.Resources

Module Example
    Public Sub Main()
        Dim resxFile As String = ".\CarResources.resx"
        Dim autos As New List(Of Automobile)
        Dim headers As New SortedList()

        Using resxReader As New ResXResourceReader(resxFile)
            For Each entry As DictionaryEntry In resxReader
                If CType(entry.Key, String).StartsWith("EarlyAuto") Then
                    autos.Add(CType(entry.Value, Automobile))
                Else If CType(entry.Key, String).StartsWith("Header") Then
                    headers.Add(CType(entry.Key, String), CType(entry.Value, String))
                End If
            Next
        End Using
        Dim headerColumns(headers.Count - 1) As String
        headers.GetValueList().CopyTo(headerColumns, 0)
        Console.WriteLine("{0,-8} {1,-10} {2,-4}   {3,-5}   {4,-9}",
                          headerColumns)
        Console.WriteLine()
        For Each 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)
        Next
    End Sub
End Module
' 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;
      }
   }
}
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Resources
Imports System.Windows.Forms

Public Class CarDisplayApp : Inherits Form
    Private Const resxFile As String = ".\CarResources.resx"
    Dim cars() As Automobile

    Public Shared Sub Main()
        Dim app As New CarDisplayApp()
        Application.Run(app)
    End Sub

    Public Sub New()
        ' Instantiate controls.
        Dim pictureBox As New PictureBox()
        pictureBox.Location = New Point(10, 10)
        Me.Controls.Add(pictureBox)
        Dim grid As New DataGridView()
        grid.Location = New Point(10, 60)
        Me.Controls.Add(grid)

        ' Get resources from .resx file.
        Using resxSet As New ResXResourceSet(resxFile)
            ' Retrieve the string resource for the title.
            Me.Text = resxSet.GetString("Title")
            ' Retrieve the image.
            Dim image As Icon = CType(resxSet.GetObject("Information", True), Icon)
            If image IsNot Nothing Then
                pictureBox.Image = image.ToBitmap()
            End If

            ' Retrieve Automobile objects.  
            Dim carList As New List(Of Automobile)
            Dim resName As String = "EarlyAuto"
            Dim auto As Automobile
            Dim ctr As Integer = 1
            Do
                auto = CType(resxSet.GetObject(resName + ctr.ToString()), Automobile)
                ctr += 1
                If auto IsNot Nothing Then carList.Add(auto)
            Loop While auto IsNot Nothing
            cars = carList.ToArray()
            grid.DataSource = cars
        End Using
    End Sub
End Class

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

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

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

 resgen.exe .resxFilename

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

vbc filename .vb -resource: .resourcesFilename

Если используется C#, синтаксис выглядит следующим образом:

 csc filename .cs -resource: .resourcesFilename

Файл .resources также можно внедрить в сателлитную сборку с помощью Assembly Linker (al.exe), который имеет следующий базовый синтаксис:

al resourcesFilename -out: assemblyFilename

См. также