System.Resources.ResourceReader 类

本文提供了此 API 参考文档的补充说明。

重要

使用不受信任的数据调用此类中的方法存在安全风险。 仅使用受信任的数据调用此类中的方法。 有关详细信息,请参阅 “验证所有输入”。

ResourceReader 类提供接口的标准实现 IResourceReaderResourceReader实例表示独立 .resources 文件或嵌入程序集中的 .resources 文件。 它用于枚举 .resources 文件中的资源并检索其名称/值对。 它不同于类 ResourceManager ,该类用于从嵌入程序集的 .resources 文件中检索指定的命名资源。 该 ResourceManager 类用于检索其名称事先已知的资源,而该 ResourceReader 类可用于检索其编号或精确名称在编译时未知的资源。 例如,应用程序可以使用资源文件来存储组织到节中的节和项中的配置信息,其中节中的节或项数事先未知。 然后,资源可以一般命名(例如Section1Section1Item1Section1Item2等),并使用ResourceReader对象进行检索。

重要

此类型实现 IDisposable 接口。 在使用完类型后,您应直接或间接释放类型。 若要直接释放类型,请在 try/catch 块中调用其 Dispose 方法。 若要间接释放类型,请使用 using(在 C# 中)或 Using(在 Visual Basic 中)等语言构造。 有关详细信息,请参阅接口文档中的“使用实现 IDisposable 的对象”部分 IDisposable

实例化 ResourceReader 对象

.resources 文件是由Resgen.exe(资源文件生成器)从文本文件或 XML .resx 文件编译的二进制文件。 对象 ResourceReader 可以表示独立 .resources 文件或嵌入程序集中的 .resources 文件。

若要实例化 ResourceReader 从独立 .resources 文件读取的对象,请使用 ResourceReader 包含 .resources 文件名的输入流或字符串的类构造函数。 下面的示例演示了这两种方法。 第一个实例化一个 ResourceReader 对象,该对象表示使用其文件名命名的 Resources1.resources .resources 文件。 第二个实例化一个 ResourceReader 对象,该对象表示使用从文件创建的流命名 Resources2.resources 的 .resources 文件。

// Instantiate a standalone .resources file from its filename.
var rr1 = new System.Resources.ResourceReader("Resources1.resources");

// Instantiate a standalone .resources file from a stream.
var fs = new System.IO.FileStream(@".\Resources2.resources",
                                  System.IO.FileMode.Open);
var rr2 = new System.Resources.ResourceReader(fs);
' Instantiate a standalone .resources file from its filename.
Dim rr1 As New System.Resources.ResourceReader("Resources1.resources")

' Instantiate a standalone .resources file from a stream.
Dim fs As New System.IO.FileStream(".\Resources2.resources",
                                   System.IO.FileMode.Open)
Dim rr2 As New System.Resources.ResourceReader(fs)

若要创建表示 ResourceReader 嵌入的 .resources 文件的对象,请从嵌入 .resources 文件的程序集实例化对象 Assembly 。 其 Assembly.GetManifestResourceStream 方法返回 Stream 可传递给 ResourceReader(Stream) 构造函数的对象。 以下示例实例化一个 ResourceReader 表示嵌入的 .resources 文件的对象。

System.Reflection.Assembly assem =
             System.Reflection.Assembly.LoadFrom(@".\MyLibrary.dll");
System.IO.Stream fs =
             assem.GetManifestResourceStream("MyCompany.LibraryResources.resources");
var rr = new System.Resources.ResourceReader(fs);
Dim assem As System.Reflection.Assembly = 
             System.Reflection.Assembly.LoadFrom(".\MyLibrary.dll") 
Dim fs As System.IO.Stream = 
             assem.GetManifestResourceStream("MyCompany.LibraryResources.resources")
Dim rr As New System.Resources.ResourceReader(fs)

枚举 ResourceReader 对象的资源

若要枚举 .resources 文件中的资源,请调用 GetEnumerator 返回对象 System.Collections.IDictionaryEnumerator 的方法。 调用 IDictionaryEnumerator.MoveNext 该方法以从一个资源移动到下一个资源。 此方法在 false 枚举 .resources 文件中的所有资源时返回。

注意

ResourceReader尽管类实现IEnumerable接口和IEnumerable.GetEnumerator方法,ResourceReader.GetEnumerator但该方法不提供IEnumerable.GetEnumerator实现。 相反,该方法 ResourceReader.GetEnumerator 返回一个 IDictionaryEnumerator 接口对象,该对象提供对每个资源的名称/值对的访问权限。

可以通过两种方式检索集合中的单个资源:

使用 IDictionaryEnumerator 属性检索资源

枚举 .resources 文件中资源的第一种方法涉及直接检索每个资源的名称/值对。 调用 IDictionaryEnumerator.MoveNext 方法移动到集合中的每个资源后,可以从属性中检索资源名称和 IDictionaryEnumerator.Key 属性中的资源数据 IDictionaryEnumerator.Value

以下示例演示如何使用 IDictionaryEnumerator.Key 和属性检索 .resources 文件中每个资源的名称和 IDictionaryEnumerator.Value 值。 若要运行该示例,请创建名为ApplicationResources.txt的以下文本文件来定义字符串资源。

Title="Contact Information"
Label1="First Name:"
Label2="Middle Name:"
Label3="Last Name:"
Label4="SSN:"
Label5="Street Address:"
Label6="City:"
Label7="State:"
Label8="Zip Code:"
Label9="Home Phone:"
Label10="Business Phone:"
Label11="Mobile Phone:"
Label12="Other Phone:"
Label13="Fax:"
Label14="Email Address:"
Label15="Alternate Email Address:"

然后,可以使用以下命令将文本资源文件转换为名为 ApplicationResources.resources 的二进制文件:

resgen ApplicationResources.txt

下面的示例然后使用 ResourceReader 该类枚举独立二进制 .resources 文件中的每个资源,并显示其键名称和相应值。

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

public class Example1
{
   public static void Run()
   {
      Console.WriteLine("Resources in ApplicationResources.resources:");
      ResourceReader res = new ResourceReader(@".\ApplicationResources.resources");
      IDictionaryEnumerator dict = res.GetEnumerator();
      while (dict.MoveNext())
         Console.WriteLine("   {0}: '{1}' (Type {2})", 
                           dict.Key, dict.Value, dict.Value.GetType().Name);
      res.Close();
   }
}
// The example displays the following output:
//       Resources in ApplicationResources.resources:
//          Label3: '"Last Name:"' (Type String)
//          Label2: '"Middle Name:"' (Type String)
//          Label1: '"First Name:"' (Type String)
//          Label7: '"State:"' (Type String)
//          Label6: '"City:"' (Type String)
//          Label5: '"Street Address:"' (Type String)
//          Label4: '"SSN:"' (Type String)
//          Label9: '"Home Phone:"' (Type String)
//          Label8: '"Zip Code:"' (Type String)
//          Title: '"Contact Information"' (Type String)
//          Label12: '"Other Phone:"' (Type String)
//          Label13: '"Fax:"' (Type String)
//          Label10: '"Business Phone:"' (Type String)
//          Label11: '"Mobile Phone:"' (Type String)
//          Label14: '"Email Address:"' (Type String)
//          Label15: '"Alternate Email Address:"' (Type String)
Imports System.Collections
Imports System.Resources

Module Example2
    Public Sub Main()
        Console.WriteLine("Resources in ApplicationResources.resources:")
        Dim res As New ResourceReader(".\ApplicationResources.resources")
        Dim dict As IDictionaryEnumerator = res.GetEnumerator()
        Do While dict.MoveNext()
            Console.WriteLine("   {0}: '{1}' (Type {2})", dict.Key, dict.Value, dict.Value.GetType().Name)
        Loop
        res.Close()
    End Sub
End Module
' The example displays output like the following:
'       Resources in ApplicationResources.resources:
'          Label3: '"Last Name:"' (Type String)
'          Label2: '"Middle Name:"' (Type String)
'          Label1: '"First Name:"' (Type String)
'          Label7: '"State:"' (Type String)
'          Label6: '"City:"' (Type String)
'          Label5: '"Street Address:"' (Type String)
'          Label4: '"SSN:"' (Type String)
'          Label9: '"Home Phone:"' (Type String)
'          Label8: '"Zip Code:"' (Type String)
'          Title: '"Contact Information"' (Type String)
'          Label12: '"Other Phone:"' (Type String)
'          Label13: '"Fax:"' (Type String)
'          Label10: '"Business Phone:"' (Type String)
'          Label11: '"Mobile Phone:"' (Type String)
'          Label14: '"Email Address:"' (Type String)
'          Label15: '"Alternate Email Address:"' (Type String)

尝试从 IDictionaryEnumerator.Value 属性检索资源数据可能会引发以下异常:

通常,如果手动修改了 .resources 文件,或者定义类型的程序集未包含在应用程序或无意中删除,或者程序集是早于类型的旧版本,则引发这些异常。 如果引发其中一个异常,可以通过枚举每个资源并调用 GetResourceData 方法来检索资源,如以下部分所示。 此方法提供有关属性尝试返回的数据类型 IDictionaryEnumerator.Value 的一些信息。

使用 GetResourceData 按名称检索资源

枚举 .resources 文件中资源的第二种方法还涉及通过调用 IDictionaryEnumerator.MoveNext 该方法浏览文件中的资源。 对于每个资源,将从属性中检索资源的名称 IDictionaryEnumerator.Key ,然后传递给 GetResourceData(String, String, Byte[]) 该方法以检索资源的数据。 这作为参数中的 resourceData 字节数组返回。

此方法比从IDictionaryEnumerator.KeyIDictionaryEnumerator.Value属性检索资源名称和值更尴尬,因为它返回构成资源值的实际字节。 但是,如果尝试检索资源引发异常,该方法 GetResourceData 可以通过提供有关资源数据类型的信息来帮助识别异常的来源。 有关指示资源数据类型的字符串的详细信息,请参阅 GetResourceData

下面的示例演示如何使用此方法检索资源并处理引发的任何异常。 它以编程方式创建一个二进制 .resources 文件,其中包含四个字符串、一个布尔值、一个整数和一个位图。 若要运行示例,请执行以下操作:

  1. 编译并执行以下源代码,该代码创建名为 ContactResources.resources 的 .resources 文件。

    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Resources;
    using System.Runtime.Versioning;
    
    public class Example5
    {
        [SupportedOSPlatform("windows")]
        public static void Run()
        {
            // Bitmap as stream.
            MemoryStream bitmapStream = new MemoryStream();
            Bitmap bmp = new Bitmap(@".\ContactsIcon.jpg");
            bmp.Save(bitmapStream, ImageFormat.Jpeg);
    
            // Define resources to be written.
            using (ResourceWriter rw = new ResourceWriter(@".\ContactResources.resources"))
            {
                rw.AddResource("Title", "Contact List");
                rw.AddResource("NColumns", 5);
                rw.AddResource("Icon", bitmapStream);
                rw.AddResource("Header1", "Name");
                rw.AddResource("Header2", "City");
                rw.AddResource("Header3", "State");
                rw.AddResource("ClientVersion", true);
                rw.Generate();
            }
        }
    }
    

    源代码文件命名为CreateResources.cs。 可以使用以下命令在 C# 中编译它:

    csc CreateResources.cs /r:library.dll
    
  2. 编译并运行以下代码以枚举 ContactResources.resources 文件中的资源。

    using System;
    using System.Collections;
    using System.Drawing;
    using System.IO;
    using System.Resources;
    using System.Runtime.Versioning;
    
    public class Example6
    {
        [SupportedOSPlatform("windows")]
        public static void Run()
        {
            ResourceReader rdr = new ResourceReader(@".\ContactResources.resources");
            IDictionaryEnumerator dict = rdr.GetEnumerator();
            while (dict.MoveNext())
            {
                Console.WriteLine("Resource Name: {0}", dict.Key);
                try
                {
                    Console.WriteLine("   Value: {0}", dict.Value);
                }
                catch (FileNotFoundException)
                {
                    Console.WriteLine("   Exception: A file cannot be found.");
                    DisplayResourceInfo(rdr, (string)dict.Key, false);
                }
                catch (FormatException)
                {
                    Console.WriteLine("   Exception: Corrupted data.");
                    DisplayResourceInfo(rdr, (string)dict.Key, true);
                }
                catch (TypeLoadException)
                {
                    Console.WriteLine("   Exception: Cannot load the data type.");
                    DisplayResourceInfo(rdr, (string)dict.Key, false);
                }
            }
        }
    
        [SupportedOSPlatform("windows")]
        private static void DisplayResourceInfo(ResourceReader rr,
                                        string key, bool loaded)
        {
            string dataType = null;
            byte[] data = null;
            rr.GetResourceData(key, out dataType, out data);
    
            // Display the data type.
            Console.WriteLine("   Data Type: {0}", dataType);
            // Display the bytes that form the available data.      
            Console.Write("   Data: ");
            int lines = 0;
            foreach (var dataItem in data)
            {
                lines++;
                Console.Write("{0:X2} ", dataItem);
                if (lines % 25 == 0)
                    Console.Write("\n         ");
            }
            Console.WriteLine();
            // Try to recreate current state of data.
            // Do: Bitmap, DateTimeTZI
            switch (dataType)
            {
                // Handle internally serialized string data (ResourceTypeCode members).
                case "ResourceTypeCode.String":
                    BinaryReader reader = new BinaryReader(new MemoryStream(data));
                    string binData = reader.ReadString();
                    Console.WriteLine("   Recreated Value: {0}", binData);
                    break;
                case "ResourceTypeCode.Int32":
                    Console.WriteLine("   Recreated Value: {0}",
                                      BitConverter.ToInt32(data, 0));
                    break;
                case "ResourceTypeCode.Boolean":
                    Console.WriteLine("   Recreated Value: {0}",
                                      BitConverter.ToBoolean(data, 0));
                    break;
                // .jpeg image stored as a stream.
                case "ResourceTypeCode.Stream":
                    const int OFFSET = 4;
                    int size = BitConverter.ToInt32(data, 0);
                    Bitmap value1 = new Bitmap(new MemoryStream(data, OFFSET, size));
                    Console.WriteLine("   Recreated Value: {0}", value1);
                    break;
                default:
                    break;
            }
            Console.WriteLine();
        }
    }
    

    修改源代码(例如,通过故意在块末尾try引发),FormatException可以运行该示例以查看调用以GetResourceData允许检索或重新创建某些资源信息。