如何从可在运行时使用的资源文件创建和调用 class 对象?
How do I create and recall class objects from a resource file that I can use at runtime?
我正在尝试创建消息代码的资源文件。我创建了一个小型控制台示例,当我尝试调用该对象时失败了。
我基于这个 MSDN -Creating Resource Files 示例,但我不知道在尝试简化它时遗漏了什么。
在我的代码中运行它曾经生成资源文件并将该文件添加到项目中。然后我重新编译 运行 调用代码。
using System;
using System.Reflection;
using System.Resources;
namespace ResourceDemo
{
internal class Program
{
private static void Main(string[] args)
{
bool generateMode = false;
if (generateMode) {
// After running once with generate mode set to true, add the resulting
// "StatusItem.resource" that was created in the .\bin\x86\Debug folder
// to the project.
Generate();
}
else {
// When run the next line generates an exception:
// An unhandled exception of type 'System.Resources.MissingManifestResourceException' occurred in mscorlib.dll
//
// Additional information: Could not find any resources appropriate for the specified culture
// or the neutral culture. Make sure "StatusItems.resources" was correctly embedded or linked
// into assembly "ResourceDemo" at compile time, or that all the satellite assemblies required
// are loadable and fully signed.
StatusItem statusItem = GetResource("2");
Console.WriteLine("Id: {0} Message: {1}", statusItem.Id.ToString(), statusItem.Message);
Console.ReadKey();
}
}
public static void Generate()
{
StatusItem[] statusItem = new StatusItem[4];
// Instantiate an Status object items.
statusItem[0] = new StatusItem(2, "File not found");
statusItem[1] = new StatusItem(3, "Path not found");
statusItem[2] = new StatusItem(4, "Too many open files");
statusItem[3] = new StatusItem(5, "File access denied");
// Define a resource file named StatusItems.resx.
using (System.Resources.ResourceWriter rw = new ResourceWriter(@".\StatusItems.resources")) {
for (int i = 0; i < 4; i++) {
rw.AddResource(statusItem[i].Id.ToString(), statusItem[i]);
}
rw.Generate();
}
}
public static StatusItem GetResource(string key)
{
Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
System.Resources.ResourceManager rm = new System.Resources.ResourceManager("StatusItems", Assembly.Load("ResourceDemo"));
return (StatusItem)rm.GetObject(key);
}
[Serializable()]
public class StatusItem
{
public StatusItem(int id, string message)
{
Id = id;
Message = message;
}
public int Id { get; set; }
public string Message { get; set; }
}
}
}
…and add that file to the project
怎么样?您是否在 IDE 中添加文件?如果是这样,那将行不通……将文件视为纯二进制数据;它不被解释为资源数据本身。您需要在命令行上使用 /resource
或使用 al.exe
在事后嵌入 .resource 文件。
如果您希望能够简单地将生成的资源输出添加到您的项目中,您可能希望使用 ResXResourceWriter
而不是 ResourceWriter
。然后您将获得一个 .resx 文件,您可以 可以 将其直接添加到项目中。 Visual Studio 将.resx 文件编译成.resources 文件并自动正确嵌入。
这还具有生成人类可读文件的优点,并且也可以在 IDE 中打开(尽管功能有限,具体取决于您放入其中的类型)。
注意事项:
ResXResourceWriter
class 实际上是在 System.Windows.Forms.dll
中定义的,因此您需要在项目中包含对该程序集的引用。
- 您写入 .resx 文件的类型需要能够在编译时被引用,这意味着它们不能在您正在编译的同一程序集中。您需要将它们放在您的程序引用的单独 DLL 中。
- .resx 文件的
ResourceManager
名称将在您的项目上下文中完全限定。因此,例如,假设您将 .resx 文件添加为项目中的顶级文件,您将需要加载 "ResourceDemo.StatusItems"
而不仅仅是 "StatusItems"
。如果您添加 .resx 文件 "As a link",它默认会在您的项目中结束,包含在与文件系统对应的文件夹中,例如"bin\Debug\StatusItems.resx"。在这种情况下,经理姓名将为 "ResourceDemo.bin.Debug.StatusItems"
.
关于最后一点,如果您对名称有任何疑问,可以使用 Assembly.GetManifestResourceNames()
检查已编译到您的程序中的名称是什么。
以下代码使用 ResourceWriter
成功创建了非嵌入式资源,并且能够使用字典调用数据对象。
从 IDE 开始似乎工作正常。我试图在命令行编译它,但 运行 变成了一些其他问题,这些问题最好留给一个单独的问题。我认为它与命令行编译有关而不是代码。
我想 post 专门回答这个问题,不过我可能会听取 Peter Duniho 的建议并改用 ResxResourceWriter
系列。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;
namespace ResourceDemo
{
internal class Program
{
private const string nameSpace = "ResourceDemo";
private const string resourceExtension = ".resources";
private const string resourceFilename = "StatusItems";
private static IDictionary<string, StatusItem> dictionary;
private static void Main(string[] args)
{
bool generateMode = false;
if (generateMode) {
// Only run when a new resource is added
Generate();
}
else {
// Show the contents of the resource
EnumerateResource();
// Make a dictionary so it is usable
BuildDictionary();
Console.WriteLine("Look-up items 2, 4, 42 and 3 in dictionary");
WriteStatusItemToConsole(GetResource("2"));
WriteStatusItemToConsole(GetResource("4"));
WriteStatusItemToConsole(GetResource("42"));
WriteStatusItemToConsole(GetResource("3"));
Console.ReadKey();
}
}
/// <summary>
/// Build the working dictionary from the resource file
/// </summary>
public static void BuildDictionary()
{
Console.WriteLine("Building a look-up dictionary");
StatusItem statusItem;
dictionary = new Dictionary<string, StatusItem>();
ResourceReader res = new ResourceReader(@".\" + resourceFilename + resourceExtension);
IDictionaryEnumerator dict = res.GetEnumerator();
while (dict.MoveNext()) {
statusItem = (StatusItem)dict.Value;
dictionary.Add(dict.Key.ToString(), statusItem);
}
res.Close();
Console.WriteLine("{0} items written to dictionary.", dictionary.Count.ToString());
Console.WriteLine();
}
/// <summary>
/// List all the items inside the resource file. Assuming that the
/// </summary>
public static void EnumerateResource()
{
StatusItem statusItem;
Console.WriteLine("Resources in {0}", resourceFilename + resourceExtension);
ResourceReader res = new ResourceReader(@".\" + resourceFilename + resourceExtension);
IDictionaryEnumerator dict = res.GetEnumerator();
Console.WriteLine("Dictionary Enumeration ready");
while (dict.MoveNext()) {
statusItem = (StatusItem)dict.Value;
Console.WriteLine(" {0}: '{1}, {2}' (Type: {3})", dict.Key, statusItem.Id.ToString(), statusItem.Message, dict.Value.GetType().Name);
}
res.Close();
Console.WriteLine();
}
/// <summary>
/// Called to create the binary resource file. Needs to be called once.
/// </summary>
public static void Generate()
{
StatusItem[] statusItem = new StatusItem[4];
// Instantiate some StatusItem objects.
statusItem[0] = new StatusItem(2, "File not found");
statusItem[1] = new StatusItem(3, "Path not found");
statusItem[2] = new StatusItem(4, "Too many open files");
statusItem[3] = new StatusItem(5, "File access denied");
// Define a resource file named StatusItems.resx.
using (System.Resources.ResourceWriter rw = new ResourceWriter(@".\" + resourceFilename + resourceExtension)) {
for (int i = 0; i < 4; i++) {
rw.AddResource(statusItem[i].Id.ToString(), statusItem[i]);
}
rw.Generate();
}
}
/// <summary>
/// Look up StatusItem in dictionary with the given key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static StatusItem GetResource(string key)
{
StatusItem result = null;
if (dictionary != null) {
dictionary.TryGetValue(key, out result);
}
return result;
}
/// <summary>
/// Write the value of the given item to the console
/// </summary>
/// <param name="statusItem"></param>
public static void WriteStatusItemToConsole(StatusItem statusItem)
{
if (statusItem != null) {
Console.WriteLine(" Id: {0} Message: {1}", statusItem.Id, statusItem.Message);
}
else {
Console.WriteLine("Null Item");
}
}
/// <summary>
/// This is our sample class
/// </summary>
[Serializable()]
public class StatusItem
{
public StatusItem(int id, string message)
{
Id = id;
Message = message;
}
public int Id { get; set; }
public string Message { get; set; }
}
}
}
我正在尝试创建消息代码的资源文件。我创建了一个小型控制台示例,当我尝试调用该对象时失败了。
我基于这个 MSDN -Creating Resource Files 示例,但我不知道在尝试简化它时遗漏了什么。
在我的代码中运行它曾经生成资源文件并将该文件添加到项目中。然后我重新编译 运行 调用代码。
using System;
using System.Reflection;
using System.Resources;
namespace ResourceDemo
{
internal class Program
{
private static void Main(string[] args)
{
bool generateMode = false;
if (generateMode) {
// After running once with generate mode set to true, add the resulting
// "StatusItem.resource" that was created in the .\bin\x86\Debug folder
// to the project.
Generate();
}
else {
// When run the next line generates an exception:
// An unhandled exception of type 'System.Resources.MissingManifestResourceException' occurred in mscorlib.dll
//
// Additional information: Could not find any resources appropriate for the specified culture
// or the neutral culture. Make sure "StatusItems.resources" was correctly embedded or linked
// into assembly "ResourceDemo" at compile time, or that all the satellite assemblies required
// are loadable and fully signed.
StatusItem statusItem = GetResource("2");
Console.WriteLine("Id: {0} Message: {1}", statusItem.Id.ToString(), statusItem.Message);
Console.ReadKey();
}
}
public static void Generate()
{
StatusItem[] statusItem = new StatusItem[4];
// Instantiate an Status object items.
statusItem[0] = new StatusItem(2, "File not found");
statusItem[1] = new StatusItem(3, "Path not found");
statusItem[2] = new StatusItem(4, "Too many open files");
statusItem[3] = new StatusItem(5, "File access denied");
// Define a resource file named StatusItems.resx.
using (System.Resources.ResourceWriter rw = new ResourceWriter(@".\StatusItems.resources")) {
for (int i = 0; i < 4; i++) {
rw.AddResource(statusItem[i].Id.ToString(), statusItem[i]);
}
rw.Generate();
}
}
public static StatusItem GetResource(string key)
{
Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
System.Resources.ResourceManager rm = new System.Resources.ResourceManager("StatusItems", Assembly.Load("ResourceDemo"));
return (StatusItem)rm.GetObject(key);
}
[Serializable()]
public class StatusItem
{
public StatusItem(int id, string message)
{
Id = id;
Message = message;
}
public int Id { get; set; }
public string Message { get; set; }
}
}
}
…and add that file to the project
怎么样?您是否在 IDE 中添加文件?如果是这样,那将行不通……将文件视为纯二进制数据;它不被解释为资源数据本身。您需要在命令行上使用 /resource
或使用 al.exe
在事后嵌入 .resource 文件。
如果您希望能够简单地将生成的资源输出添加到您的项目中,您可能希望使用 ResXResourceWriter
而不是 ResourceWriter
。然后您将获得一个 .resx 文件,您可以 可以 将其直接添加到项目中。 Visual Studio 将.resx 文件编译成.resources 文件并自动正确嵌入。
这还具有生成人类可读文件的优点,并且也可以在 IDE 中打开(尽管功能有限,具体取决于您放入其中的类型)。
注意事项:
ResXResourceWriter
class 实际上是在System.Windows.Forms.dll
中定义的,因此您需要在项目中包含对该程序集的引用。- 您写入 .resx 文件的类型需要能够在编译时被引用,这意味着它们不能在您正在编译的同一程序集中。您需要将它们放在您的程序引用的单独 DLL 中。
- .resx 文件的
ResourceManager
名称将在您的项目上下文中完全限定。因此,例如,假设您将 .resx 文件添加为项目中的顶级文件,您将需要加载"ResourceDemo.StatusItems"
而不仅仅是"StatusItems"
。如果您添加 .resx 文件 "As a link",它默认会在您的项目中结束,包含在与文件系统对应的文件夹中,例如"bin\Debug\StatusItems.resx"。在这种情况下,经理姓名将为"ResourceDemo.bin.Debug.StatusItems"
.
关于最后一点,如果您对名称有任何疑问,可以使用 Assembly.GetManifestResourceNames()
检查已编译到您的程序中的名称是什么。
以下代码使用 ResourceWriter
成功创建了非嵌入式资源,并且能够使用字典调用数据对象。
从 IDE 开始似乎工作正常。我试图在命令行编译它,但 运行 变成了一些其他问题,这些问题最好留给一个单独的问题。我认为它与命令行编译有关而不是代码。
我想 post 专门回答这个问题,不过我可能会听取 Peter Duniho 的建议并改用 ResxResourceWriter
系列。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;
namespace ResourceDemo
{
internal class Program
{
private const string nameSpace = "ResourceDemo";
private const string resourceExtension = ".resources";
private const string resourceFilename = "StatusItems";
private static IDictionary<string, StatusItem> dictionary;
private static void Main(string[] args)
{
bool generateMode = false;
if (generateMode) {
// Only run when a new resource is added
Generate();
}
else {
// Show the contents of the resource
EnumerateResource();
// Make a dictionary so it is usable
BuildDictionary();
Console.WriteLine("Look-up items 2, 4, 42 and 3 in dictionary");
WriteStatusItemToConsole(GetResource("2"));
WriteStatusItemToConsole(GetResource("4"));
WriteStatusItemToConsole(GetResource("42"));
WriteStatusItemToConsole(GetResource("3"));
Console.ReadKey();
}
}
/// <summary>
/// Build the working dictionary from the resource file
/// </summary>
public static void BuildDictionary()
{
Console.WriteLine("Building a look-up dictionary");
StatusItem statusItem;
dictionary = new Dictionary<string, StatusItem>();
ResourceReader res = new ResourceReader(@".\" + resourceFilename + resourceExtension);
IDictionaryEnumerator dict = res.GetEnumerator();
while (dict.MoveNext()) {
statusItem = (StatusItem)dict.Value;
dictionary.Add(dict.Key.ToString(), statusItem);
}
res.Close();
Console.WriteLine("{0} items written to dictionary.", dictionary.Count.ToString());
Console.WriteLine();
}
/// <summary>
/// List all the items inside the resource file. Assuming that the
/// </summary>
public static void EnumerateResource()
{
StatusItem statusItem;
Console.WriteLine("Resources in {0}", resourceFilename + resourceExtension);
ResourceReader res = new ResourceReader(@".\" + resourceFilename + resourceExtension);
IDictionaryEnumerator dict = res.GetEnumerator();
Console.WriteLine("Dictionary Enumeration ready");
while (dict.MoveNext()) {
statusItem = (StatusItem)dict.Value;
Console.WriteLine(" {0}: '{1}, {2}' (Type: {3})", dict.Key, statusItem.Id.ToString(), statusItem.Message, dict.Value.GetType().Name);
}
res.Close();
Console.WriteLine();
}
/// <summary>
/// Called to create the binary resource file. Needs to be called once.
/// </summary>
public static void Generate()
{
StatusItem[] statusItem = new StatusItem[4];
// Instantiate some StatusItem objects.
statusItem[0] = new StatusItem(2, "File not found");
statusItem[1] = new StatusItem(3, "Path not found");
statusItem[2] = new StatusItem(4, "Too many open files");
statusItem[3] = new StatusItem(5, "File access denied");
// Define a resource file named StatusItems.resx.
using (System.Resources.ResourceWriter rw = new ResourceWriter(@".\" + resourceFilename + resourceExtension)) {
for (int i = 0; i < 4; i++) {
rw.AddResource(statusItem[i].Id.ToString(), statusItem[i]);
}
rw.Generate();
}
}
/// <summary>
/// Look up StatusItem in dictionary with the given key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static StatusItem GetResource(string key)
{
StatusItem result = null;
if (dictionary != null) {
dictionary.TryGetValue(key, out result);
}
return result;
}
/// <summary>
/// Write the value of the given item to the console
/// </summary>
/// <param name="statusItem"></param>
public static void WriteStatusItemToConsole(StatusItem statusItem)
{
if (statusItem != null) {
Console.WriteLine(" Id: {0} Message: {1}", statusItem.Id, statusItem.Message);
}
else {
Console.WriteLine("Null Item");
}
}
/// <summary>
/// This is our sample class
/// </summary>
[Serializable()]
public class StatusItem
{
public StatusItem(int id, string message)
{
Id = id;
Message = message;
}
public int Id { get; set; }
public string Message { get; set; }
}
}
}