System.Resources.ResourceReader 类

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

重要

使用不受信任的数据调用此类中的方法存在安全风险。 仅在确保数据受信任的情况下,再从该类调用方法。 有关详细信息,请参阅验证所有输入

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

重要

此类型实现 IDisposable 接口。 使用完该类型后,应直接或间接处理它。 若要直接释放类型,请在块中Disposetry/调用其catch方法。 若要间接释放它,请使用语言构造,例如 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 对象,该对象表示一个 .resources 文件,该文件使用从文件创建的流来命名 Resources2.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)

为了创建一个表示嵌入 .resources 文件的 ResourceReader 对象,请从嵌入了 .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 该方法以从一个资源移动到下一个资源。 当 .resources 文件中的所有资源已被枚举完毕时,此方法返回 false

注释

尽管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($"   {dict.Key}: '{dict.Value}' (Type {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: {dict.Key}");
                try
                {
                    Console.WriteLine($"   Value: {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: {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: {binData}");
                    break;
                case "ResourceTypeCode.Int32":
                    Console.WriteLine($"   Recreated Value: {BitConverter.ToInt32(data, 0)}");
                    break;
                case "ResourceTypeCode.Boolean":
                    Console.WriteLine($"   Recreated Value: {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: {value1}");
                    break;
                default:
                    break;
            }
            Console.WriteLine();
        }
    }
    

    修改源代码(例如,通过故意在FormatException块的末尾抛出try),然后运行该示例,以查看调用GetResourceData如何使您能够检索或重新创建某些资源信息。