本文提供了此 API 参考文档的补充说明。
重要
使用不受信任的数据调用此类中的方法存在安全风险。 仅在确保数据受信任的情况下,再从该类调用方法。 有关详细信息,请参阅验证所有输入。
ResourceReader 类提供 IResourceReader 接口的标准实现。 ResourceReader实例表示一个独立的.resources文件或嵌入在程序集中的 .resources 文件。 它用于枚举 .resources 文件中的资源并检索其名称/值对。 它不同于类 ResourceManager ,该类用于从嵌入程序集的 .resources 文件中检索指定的命名资源。 该 ResourceManager 类用于检索其名称事先已知的资源,而该 ResourceReader 类可用于检索其编号或精确名称在编译时未知的资源。 例如,应用程序可以使用资源文件存储配置信息,这些信息按节和节内的项目组织,其中节的数量或每个节中的项目数事先未知。 然后,资源可以一般命名(例如Section1
,Section1Item1
Section1Item2
等),并使用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 接口对象,该对象提供对每个资源的名称/值对的访问权限。
可以通过两种方式检索集合中的单个资源:
可以循环访问集合中的每个 System.Collections.IDictionaryEnumerator 资源,并使用 System.Collections.IDictionaryEnumerator 属性检索资源名称和值。 当所有资源都属于同一类型或知道每个资源的数据类型时,我们建议使用此方法。
循环访问 System.Collections.IDictionaryEnumerator 集合并调用 GetResourceData 方法检索资源数据时,可以检索每个资源的名称。 如果不知道每个资源的数据类型,或者前面的方法是否引发异常,我们建议使用此方法。
使用 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 属性检索资源数据可能会引发以下异常:
- 如果数据不采用预期格式,则为 A FormatException 。
- 如果找不到包含数据所属类型的程序集,则返回 FileNotFoundException。
- 如果找不到数据所属的类型,则返回 TypeLoadException。
通常,如果手动修改了 .resources 文件,或者定义类型的程序集未包含在应用程序或无意中删除,或者程序集是早于类型的旧版本,则引发这些异常。 如果引发其中一个异常,可以通过枚举每个资源并调用 GetResourceData 方法来检索资源,如以下部分所示。 此方法为您提供 IDictionaryEnumerator.Value 属性尝试返回的数据类型的一些信息。
使用 GetResourceData 按名称检索资源
枚举 .resources 文件中资源的第二种方法还涉及通过调用 IDictionaryEnumerator.MoveNext
该方法浏览文件中的资源。 对于每个资源,将从属性中检索资源的名称 IDictionaryEnumerator.Key ,然后传递给 GetResourceData(String, String, Byte[]) 该方法以检索资源的数据。 这作为参数中的 resourceData
字节数组返回。
此方法比从IDictionaryEnumerator.KeyIDictionaryEnumerator.Value属性检索资源名称和值更尴尬,因为它返回构成资源值的实际字节。 但是,如果尝试检索资源引发异常,该方法 GetResourceData 可以通过提供有关资源数据类型的信息来帮助识别异常的来源。 有关指示资源数据类型的字符串的详细信息,请参阅 GetResourceData。
下面的示例演示如何使用此方法检索资源并处理引发的任何异常。 它以编程方式创建一个二进制 .resources 文件,其中包含四个字符串、一个布尔值、一个整数和一个位图。 若要运行示例,请执行以下作:
编译并执行以下源代码,该代码创建名为 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
编译并运行以下代码以枚举 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如何使您能够检索或重新创建某些资源信息。