以编程方式使用 .resx 文件Work with .resx files programmatically

由于 XML 资源 (.resx) 文件必须由定义完善的 XML 组成,这些 XML 的标头必须遵循特定架构(后跟名称/值对的数据),因此你会发现手动创建这些文件很容易出错。Because XML resource (.resx) files must consist of well-defined XML, including a header that must follow a specific schema followed by data in name/value pairs, you may find that creating these files manually is error-prone. 作为一种替代方法,可以使用 .NET 类库中的类型和成员以编程方式创建 .resx 文件。As an alternative, you can create .resx files programmatically by using types and members in the .NET Class Library. 还可以使用 .NET 类库来检索存储在 .resx 文件中的资源。You can also use the .NET Class Library to retrieve resources that are stored in .resx files. 本文说明如何使用 System.Resources 命名空间的类型和成员来操作 .resx 文件。This article explains how you can use the types and members in the System.Resources namespace to work with .resx files.

本文讨论的是如何操作包含资源的 XML (.resx) 文件。This article discusses working with XML (.resx) files that contain resources. 有关操作嵌入程序集中的二进制资源文件的信息,请参阅 ResourceManagerFor information on working with binary resource files that have been embedded in assemblies, see ResourceManager.

警告

除编程方式以外还可以使用其他方式操作 .resx 文件。There are also ways to work with .resx files other than programmatically. 如果将资源文件添加到 Visual Studio 项目,那么 Visual Studio 会提供一个用于创建和维护 .resx 文件的接口,并且在编译时自动将 .resx 文件转换为 .resources 文件。When you add a resource file to a Visual Studio project, Visual Studio provides an interface for creating and maintaining a .resx file, and automatically converts the .resx file to a .resources file at compile time. 你还可以使用文本编辑器来直接操作 .resx 文件。You can also use a text editor to manipulate a .resx file directly. 但是,若要避免破坏文件,请注意不要修改存储在文件中的任何二进制信息。However, to avoid corrupting the file, be careful not to modify any binary information that is stored in the file.

创建 .resx 文件Create a .resx file

你可以使用 System.Resources.ResXResourceWriter 类以编程方式创建 .resx 文件,步骤如下:You can use the System.Resources.ResXResourceWriter class to create a .resx file programmatically, by following these steps:

  1. 通过调用 ResXResourceWriter 方法和提供 .resx 文件的名称实例化 ResXResourceWriter(String) 对象。Instantiate a ResXResourceWriter object by calling the ResXResourceWriter(String) method and supplying the name of the .resx file. 此文件名必须包括 .resx 扩展名。The file name must include the .resx extension. 如果实例化位于 ResXResourceWriter 块中的 using 对象,则无需在步骤 3 中显式调用 ResXResourceWriter.Close 方法。If you instantiate the ResXResourceWriter object in a using block, you do not explicitly have to call the ResXResourceWriter.Close method in step 3.

  2. 为每个要添加到此文件中的资源调用 ResXResourceWriter.AddResource 方法。Call the ResXResourceWriter.AddResource method for each resource you want to add to the file. 使用此方法的重载添加字符串、 对象和二进制(字节数组)数据。Use the overloads of this method to add string, object, and binary (byte array) data. 如果资源是一个对象,则它必须是可序列化的。If the resource is an object, it must be serializable.

  3. 调用 ResXResourceWriter.Close 方法以生成资源文件并释放所有资源。Call the ResXResourceWriter.Close method to generate the resource file and to release all resources. 如果 ResXResourceWriter 对象是在 using 块中创建的,那么资源将写入 .resx 文件,并且在 ResXResourceWriter 块的末尾释放 using 对象所使用的资源。If the ResXResourceWriter object was created within a using block, resources are written to the .resx file and the resources used by the ResXResourceWriter object are released at the end of the using block.

生成的 .resx 文件具有相应的标头,并且由 data 方法添加的每个资源都有一个 ResXResourceWriter.AddResource 标记。The resulting .resx file has the appropriate header and a data tag for each resource added by the ResXResourceWriter.AddResource method.

警告

请勿使用资源文件存储密码、 安全敏感信息或私人数据。Do not use resource files to store passwords, security-sensitive information, or private data.

下面的示例创建了一个名为 CarResources.resx 的 .resx 文件,该文件存储了六个字符串、一个图标和两个由应用程序定义的对象(两个 Automobile 对象)。The following example creates a .resx file named CarResources.resx that stores six strings, an icon, and two application-defined objects (two Automobile objects). 本示例中定义并实例化的 Automobile 类使用 SerializableAttribute 属性进行标记。The Automobile class, which is defined and instantiated in the example, is tagged with the SerializableAttribute attribute.

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 文件。You can also use Visual Studio to create .resx files. 在编译时,Visual Studio 使用 资源文件生成器 (Resgen.exe) 将 .resx 文件转换为二进制资源 (.resources) 文件,然后还将其嵌入应用程序集或附属程序集。At compile time, Visual Studio uses the Resource File Generator (Resgen.exe) to convert the .resx file to a binary resource (.resources) file, and also embeds it in either an application assembly or a satellite assembly.

你无法将 .resx 文件嵌入运行时可执行文件中或将其编译到附属程序集。You cannot embed a .resx file in a runtime executable or compile it into a satellite assembly. 必须使用 资源文件生成器 (Resgen.exe)将 .resx 文件转换为二进制资源 (.resources) 文件 。You must convert your .resx file into a binary resource (.resources) file by using the Resource File Generator (Resgen.exe). 然后可以将生成的 .resources 文件嵌入应用程序集或附属程序集。The resulting .resources file can then be embedded in an application assembly or a satellite assembly. 有关详细信息,请参阅 Creating Resource FilesFor more information, see Creating Resource Files.

枚举资源Enumerate resources

在某些情况下,你可能想要从 .resx 文件中检索所有资源,而不是某个特定资源。In some cases, you may want to retrieve all resources, instead of a specific resource, from a .resx file. 若要执行此操作,可以使用 System.Resources.ResXResourceReader 类,该类为 .resx 文件中的所有资源提供枚举器。To do this, you can use the System.Resources.ResXResourceReader class, which provides an enumerator for all resources in the .resx file. System.Resources.ResXResourceReader 类将实现 IDictionaryEnumerator,并返回 DictionaryEntry 对象,该对象代表用于循环的每个迭代的特定资源。The System.Resources.ResXResourceReader class implements IDictionaryEnumerator, which returns a DictionaryEntry object that represents a particular resource for each iteration of the loop. 此对象的 DictionaryEntry.Key 属性返回资源的键, DictionaryEntry.Value 属性返回资源的值。Its DictionaryEntry.Key property returns the resource's key, and its DictionaryEntry.Value property returns the resource's value.

下面的示例为前面的示例中创建的 CarResources.resx 文件创建 ResXResourceReader 对象,并在整个资源文件中进行迭代。The following example creates a ResXResourceReader object for the CarResources.resx file created in the previous example and iterates through the resource file. 该示例将资源文件中定义的两个 Automobile 对象添加到 System.Collections.Generic.List<T> 对象,以及将六个字符串中的五个添加到 SortedList 对象。It adds the two Automobile objects that are defined in the resource file to a System.Collections.Generic.List<T> object, and it adds five of the six strings to a SortedList object. SortedList 对象中的值将转换为一个参数数组,该参数数组用于向控制台显示列标题。The values in the SortedList object are converted to a parameter array, which is used to display column headings to the console. 还将向控制台显示 Automobile 属性值。The Automobile property values are also displayed to the console.

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

检索特定的资源Retrieve a specific resource

除了枚举 .resx 文件中的项,你可以使用 System.Resources.ResXResourceSet 类按名称检索特定资源。In addition to enumerating the items in a .resx file, you can retrieve a specific resource by name by using the System.Resources.ResXResourceSet class. ResourceSet.GetString(String) 方法用于检索命名字符串资源的值。The ResourceSet.GetString(String) method retrieves the value of a named string resource. ResourceSet.GetObject(String) 方法用于检索命名对象或二进制数据的值。The ResourceSet.GetObject(String) method retrieves the value of a named object or binary data. 该方法返回的对象之后必须转换为(C# 中的 cast 或 Visual Basic 中的 convert)相应类型的对象。The method returns an object that must then be cast (in C#) or converted (in Visual Basic) to an object of the appropriate type.

以下示例按资源名称检索窗体的标题字符串和图标,The following example retrieves a form's caption string and icon by their resource names. 还检索之前示例中使用的应用程序定义的 Automobile 对象,并在 DataGridView 控件中显示这些对象。It also retrieves the application-defined Automobile objects used in the previous example and displays them in a DataGridView control.

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 文件Convert .resx files to binary .resources files

将 .resx 文件转换为嵌入式二进制资源 (.resources) 文件可以带来明显的好处。Converting .resx files to embedded binary resource (.resources) files has significant advantages. 虽然 .resx 文件容易阅读,并且在应用程序开发期间易于维护,但是完成的应用程序很少包含这些文件。Although .resx files are easy to read and maintain during application development, they are rarely included with finished applications. 如果这些文件随应用程序分发,那么它们将作为单独文件存在,与应用程序可执行文件和随附的库分开存放。If they are distributed with an application, they exist as separate files apart from the application executable and its accompanying libraries. 与此相反,.resources 文件则嵌入应用程序可执行文件或其随附的程序集中。In contrast, .resources files are embedded in the application executable or its accompanying assemblies. 另外,对于在运行时依赖 .resx 文件的本地化应用程序,开发人员负责处理资源回退。In addition, for localized applications, relying on .resx files at run time places the responsibility for handling resource fallback on the developer. 与此相反,如果创建了一组包含嵌入式 .resources 文件的附属程序集,则公共语言运行时将处理资源回退进程。In contrast, if a set of satellite assemblies that contain embedded .resources files has been created, the common language runtime handles the resource fallback process.

要将 .resx 文件转换为 .resources 文件,请使用 资源文件生成器 (Resgen.exe),它具有以下基本语法:To convert a .resx file to a .resources file, you use the Resource File Generator (Resgen.exe), which has the following basic syntax:

Resgen.exe .resxFilenameResgen.exe .resxFilename

这会生成一个具有与 .resx 文件相同的根文件名的二进制资源文件,并且该文件的扩展名为 .resources。The result is a binary resource file that has the same root file name as the .resx file and a .resources file extension. 然后,可在编译时将该文件编译为可执行文件或库。This file can then be compiled into an executable or a library at compile time. 如果你使用的是 Visual Basic 编译器,则使用以下语法以在应用程序的可执行文件中嵌入一个 .resources 文件:If you are using the Visual Basic compiler, use the following syntax to embed a .resources file in an application's executable:

vbc filename .vb -resource: .resourcesFilenamevbc filename .vb -resource: .resourcesFilename

如果使用的是 C#,则语法如下所示:If you are using C#, the syntax is as follows:

csc filename .cs -resource: .resourcesFilenamecsc filename .cs -resource: .resourcesFilename

还可以使用 程序集链接器 (AL.exe)在附属程序集中嵌入 .resources 文件,基本语法如下:The .resources file can also be embedded in a satellite assembly by using Assembly Linker (AL.exe), which has the following basic syntax:

al resourcesFilename -out: assemblyFilenameal resourcesFilename -out: assemblyFilename

请参阅See also