扩展的强类型资源生成器






4.87/5 (73投票s)
扩展版本的强类型资源生成器,支持格式化。
引言
Visual Studio .NET 2005/2008 IDE 的一项出色新功能是一个名为 ResXFileCodeGenerator 的自定义工具,每次将资源(*.resx 文件)添加到项目中时,都会自动与其关联。每当您的项目重建、保存资源文件或手动运行自定义工具时,该工具都会生成一个托管类,该类将您在 *.resx 文件中的每个资源公开为强类型的静态属性。现在,支持的任何类型的资源(包括图像、图标、字符串等)都可以轻松检索。
下面的两个屏幕截图说明了添加到 Visual Studio .NET 2005 的资源文件的默认属性,以及依赖于 Resource.resx 并由 ResXFileCodeGenerator 自定义工具自动生成的 Resource.Designer.cs 源文件。
![]() |
![]() |
生成的类公开的所有属性始终是静态的。该类公开以下属性:
ResourceManager
属性,返回类型为System.Resources.ResourceManager
,用于在运行时访问特定于区域性的资源。Culture
属性,返回类型为System.Globalization.CultureInfo
,并具有get
和set
访问器。Culture
属性的set
访问器可用于指定资源本地化的必需区域性。默认情况下,Culture
属性返回null
,这意味着文化信息是通过Culture
的CurrentUICulture
属性获得的。- 用于资源访问的属性,名称与相应资源的名称相同(属性名称可根据所使用的代码生成器要求进行调整)。它们的类型对应于 *.resx 文件中的资源类型。
如果您的资源文件仅包含一个字符串资源(如上图所示),则生成的类将如下所示:
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[GeneratedCodeAttribute("Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
[DebuggerNonUserCodeAttribute()]
[CompilerGeneratedAttribute()]
internal class Resource {
private static ResourceManager resourceMan;
private static CultureInfo resourceCulture;
[SuppressMessageAttribute(
"Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
internal static ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
ResourceManager temp =
new ResourceManager("MyApp.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
internal static CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Message text.
/// </summary>
internal static string Message {
get {
return ResourceManager.GetString("Message", resourceCulture);
}
}
}
背景
尽管 ResXFileCodeGenerator 自定义工具极大地简化了资源访问过程,但我们可以指出以下四个主要缺点:
- 由 ResXFileCodeGenerator 自定义工具生成的强类型资源类始终具有内部可见性。由于生成的类被标记为
internal
,因此除了 友好程序集 之外,无法从其他程序集访问它。但是,使用 /publicClass 选项的 resgen.exe 实用程序会将强类型资源类生成为公共类,但此时自定义工具的所有优势都将丢失。 - 在大多数情况下,*.resx 文件仅包含字符串,包括 .NET Framework 格式化机制 使用的格式字符串(包含零个或多个格式项的字符串)。从资源加载格式字符串并为其传递正确数量的参数一直是一个问题。传递不正确的参数数量不会导致编译错误,而是会导致恼人的运行时错误。
ResourceManager
属性中ResourceManager
类实例的线程不安全初始化。- 无法通过资源类包装器访问资源名称。
- 生成的资源类包装器与 .NET Compact Framework 不兼容。
注意: Visual Studio .NET 2008 引入了一个名为 PublicResXFileCodeGenerator 的新自定义工具,用于生成公共资源类包装器。
扩展强类型资源生成器
鉴于上述 ResXFileCodeGenerator 的缺点,我们决定开发一个扩展版本的强类型资源生成器,以弥补现有 ResXFileCodeGenerator 自定义工具的不足。
使用扩展版本的强类型资源生成器非常简单,与使用 Visual Studio .NET 2005 和 2008 附带的资源代码生成器没有区别。扩展强类型资源生成器由两个新的自定义工具表示:
- ResXFileCodeGeneratorEx:一个生成公共资源包装器的自定义工具。
- InternalResXFileCodeGeneratorEx:一个生成内部资源包装器的自定义工具。
首先,您必须在您的计算机上安装和注册扩展的强类型资源生成器(例如,ResXFileCodeGeneratorEx 和 InternalResXFileCodeGeneratorEx 自定义工具)。请记住,您必须拥有管理员权限才能安装和注册新的 Visual Studio .NET 自定义工具。有两种方法可以在您的计算机上注册扩展的强类型资源生成器:
- 首选方法是下载 Windows Installer 包并将其安装到计算机上的特定位置。安装程序将自动在您的计算机上注册 ResXFileCodeGeneratorEx 和 InternalResXFileCodeGeneratorEx Visual Studio .NET 自定义工具。建议解压缩安装程序压缩包的内容并运行 Setup.exe 来启动安装。这种安装方式与 Vista 兼容。
- 从提供的压缩包中获取源代码并重新编译它们。如果成功,ResXFileCodeGeneratorEx 和 InternalResXFileCodeGeneratorEx 自定义工具将在您的 PC 上注册。为了使工具正常工作,您必须将生成输出保留在输出目录中,因为 Visual Studio .NET 自定义工具是 COM 对象,并且应该保留在它们注册的同一目录中。
安装扩展强类型资源生成器后,您必须重新启动所有运行的 Visual Studio .NET 2005 和 2008 实例。
从这一点开始,您可以在您的项目中使用扩展强类型资源生成器的所有优势。您可以手动将 ResXFileCodeGeneratorEx 或 InternalResXFileCodeGeneratorEx 指定为资源文件的自定义工具,或者您可以调整默认的 Visual Studio .NET 2005/2008 项模板。
让我们以 MyApp 项目为例,添加另一个包含格式化字符串的资源条目。最重要的一步是将自定义工具名称更改为扩展强类型资源生成器(ResXFileCodeGeneratorEx 或 InternalResXFileCodeGeneratorEx)。通过保存资源文件或手动运行自定义工具来运行自定义工具。您必须在 Visual Studio .NET 中右键单击资源文件,然后在下拉菜单中选择 Run Custom Tool。
ResXFileCodeGeneratorEx 自定义工具生成的资源包装器类如下例所示:
/// <summary>
/// A strongly-typed resource class, for looking up localized strings,
/// formatting them, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilderEx class via the
// ResXFileCodeGeneratorEx custom tool. To add or remove a member, edit your .ResX file
// then rerun the ResXFileCodeGeneratorEx custom tool or rebuild your VS.NET project.
// Copyright (c) Dmytro Kryvko 2006-2008 (http://dmytro.kryvko.googlepages.com/)
[GeneratedCodeAttribute
("DMKSoftware.CodeGenerators.Tools.StronglyTypedResourceBuilderEx", "2.3.0.0")]
[DebuggerNonUserCodeAttribute()]
[SuppressMessageAttribute
("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
public partial class Resource {
private static ResourceManager _resourceManager;
private static object _internalSyncObject;
private static CultureInfo _resourceCulture;
[SuppressMessageAttribute
("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public Resource() {
}
/// <summary>
/// Thread safe lock object used by this class.
/// </summary>
public static object InternalSyncObject {
get {
if (object.ReferenceEquals(_internalSyncObject, null)) {
Interlocked.CompareExchange
(ref _internalSyncObject, new object(), null);
}
return _internalSyncObject;
}
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
public static ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(_resourceManager, null)) {
Monitor.Enter(InternalSyncObject);
try {
if (object.ReferenceEquals(_resourceManager, null)) {
Interlocked.Exchange(ref _resourceManager,
new ResourceManager("MyApp.Resource",
typeof(Resource).Assembly));
}
}
finally {
Monitor.Exit(InternalSyncObject);
}
}
return _resourceManager;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
public static CultureInfo Culture {
get {
return _resourceCulture;
}
set {
_resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to 'Hello, {0}!'.
/// </summary>
public static string Hello {
get {
return ResourceManager.GetString(ResourceNames.Hello, _resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 'Message text'.
/// </summary>
public static string Message {
get {
return ResourceManager.GetString
(ResourceNames.Message, _resourceCulture);
}
}
/// <summary>
/// Formats a localized string similar to 'Hello, {0}!'.
/// </summary>
/// <param name="arg0">An object (0) to format.</param>
/// <returns>A copy of format string in which the format
/// items have been replaced by the String equivalent of
/// the corresponding instances of Object in arguments.</returns>
public static string HelloFormat(object arg0) {
return string.Format(_resourceCulture, Hello, arg0);
}
/// <summary>
/// Lists all the resource names as constant string fields.
/// </summary>
public class ResourceNames {
/// <summary>
/// Stores the resource name 'Hello'.
/// </summary>
public const string Hello = "Hello";
/// <summary>
/// Stores the resource name 'Message'.
/// </summary>
public const string Message = "Message";
}
}
正如您所见,生成的类是 public
的,这允许您在程序集之间共享资源。但是,主要区别在于添加了一个名为 HelloFormat
的附加方法。此方法是对 Hello
资源条目字符串值进行分析和验证的结果。扩展强类型资源生成器会自动确定资源字符串值是否为有效的 .NET Framework 格式字符串,并相应地生成代码。
格式方法的名称始终按以下方式生成:资源属性加上 Format 后缀。参数数量会自动计算,等于 String.Format()
方法期望的参数数量。另一方面,仍然可以通过公开的 Hello
属性获取格式字符串。如上所述,扩展强类型资源生成器会执行格式字符串验证。例如,您可能会错误地编写一个无效的格式字符串,如:Hello, {{0}
。 (内部)ResXFileCodeGeneratorEx 自定义工具将解析无效格式并向您显示有关此问题的警告。在这种特定情况下,将不会生成格式方法,但资源访问属性仍将保留在生成的类中。
标准 Visual Studio 资源包装器生成器的一组小的改进:
- 资源包装器类中没有
[CompilerGeneratedAttribute()]
,这使其与 .NET Compact Framework 兼容。 - 生成嵌套类
ResourceNames
,将所有资源名称定义为字符串常量。嵌套类的可见性与其父类的可见性相同。
生成公共资源类包装器几乎适合所有人,但是,有些人仍然希望能够生成内部资源包装器。因此,版本 2.1 引入了 InternalResXFileCodeGeneratorEx Visual Studio .NET 自定义工具,用于生成强类型的内部资源包装器。InternalResXFileCodeGeneratorEx 的输出如下例所示:
/// <summary>
/// A strongly-typed resource class, for looking up localized strings,
/// formatting them, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilderEx class via the
// InternalResXFileCodeGeneratorEx custom tool.
// To add or remove a member, edit your .ResX file
// then rerun the InternalResXFileCodeGeneratorEx custom tool or
// rebuild your VS.NET project.
// Copyright (c) Dmytro Kryvko 2006-2008 (http://dmytro.kryvko.googlepages.com/)
[GeneratedCodeAttribute
("DMKSoftware.CodeGenerators.Tools.StronglyTypedResourceBuilderEx", "2.3.0.0")]
[DebuggerNonUserCodeAttribute()]
[SuppressMessageAttribute
("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
internal partial class Resource {
private static ResourceManager _resourceManager;
private static object _internalSyncObject;
private static CultureInfo _resourceCulture;
[SuppressMessageAttribute
("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// Thread safe lock object used by this class.
/// </summary>
internal static object InternalSyncObject {
get {
if (object.ReferenceEquals(_internalSyncObject, null)) {
Interlocked.CompareExchange
(ref _internalSyncObject, new object(), null);
}
return _internalSyncObject;
}
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
internal static ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(_resourceManager, null)) {
Monitor.Enter(InternalSyncObject);
try {
if (object.ReferenceEquals(_resourceManager, null)) {
Interlocked.Exchange(ref _resourceManager,
new ResourceManager("MyApp.Resource",
typeof(Resource).Assembly));
}
}
finally {
Monitor.Exit(InternalSyncObject);
}
}
return _resourceManager;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
internal static CultureInfo Culture {
get {
return _resourceCulture;
}
set {
_resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to 'Hello, {0}!'.
/// </summary>
internal static string Hello {
get {
return ResourceManager.GetString(ResourceNames.Hello, _resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 'Message text'.
/// </summary>
internal static string Message {
get {
return ResourceManager.GetString(ResourceNames.Message, _resourceCulture);
}
}
/// <summary>
/// Formats a localized string similar to 'Hello, {0}!'.
/// </summary>
/// <param name="arg0">An object (0) to format.</param>
/// <returns>A copy of format string in which the format
/// items have been replaced by the String equivalent of
/// the corresponding instances of Object in arguments.</returns>
internal static string HelloFormat(object arg0) {
return string.Format(_resourceCulture, Hello, arg0);
}
/// <summary>
/// Lists all the resource names as constant string fields.
/// </summary>
internal class ResourceNames {
/// <summary>
/// Stores the resource name 'Hello'.
/// </summary>
internal const string Hello = "Hello";
/// <summary>
/// Stores the resource name 'Message'.
/// </summary>
internal const string Message = "Message";
}
}
历史
- 2.6 - 2009 年 3 月 30 日
- 已恢复 Silverlight 兼容性。由于在类级别添加了
ObfuscationAttribute()
属性,它以前被破坏了。感谢 Eric Smith 和 r2musings 报告此问题。 - 已移除 J# 支持。
- 2.5 - 2009 年 2 月 20 日
- 已将
ObfuscationAttribute()
添加到生成的资源包装器类(感谢 Friedhelm)。 - 已解决资源包装器类构造函数缺少 XML 文档的问题(感谢 Casey Barton)。
- 2.4 - 2008 年 10 月 20 日
- 添加了对 Visual Studio Express 2005 和 2008 版本 (感谢 Fabien Letort 和 Ondrej Bohaciak)。
- 2.3 - 2008 年 10 月 7 日
- 添加了嵌套类
ResourceNames
,列出了所有资源名称作为常量。 - 已移除无参数 {PROPERTY_NAME}
Format()
方法的生成。关于它们的存在存在一些混淆,所以显然这不是一个好主意,抱歉! - 生成的资源包装器类已设为 partial(感谢 DameonBlack)。
- 通过将资源包装器类的构造函数设为
public
来添加 Silverlight 兼容性(感谢 Slyi)。 - 已修复安装程序中的语言问题(感谢 Jasoncd)。
- 改进了资源包装器生成性能。
- 2.2 - 2008 年 5 月 24 日
- 已修复在某些情况下生成重复格式方法的问题(感谢 Doug Richardson)。
- 所有类型都作为全局空间的成员进行引用(感谢 Doug Richardson)。
- 已添加对
InternalSyncObject
属性的缺失注释(感谢 Jesse Napier)。 - 已添加抑制代码分析警告的属性(感谢 Jesse Napier)。
- 已执行代码重构。
- 2.1 - 2008 年 2 月 14 日
- 已添加 InternalResXFileCodeGeneratorEx 自定义工具(感谢 Bernd Hoffmann)。
- 生成的资源包装器类不再是
sealed
(感谢 Andrea from Italy 和 Miki from Germany)。 - 已修复资源包装器类构造函数上的
SuppressMessage
属性的问题。 - 在资源包装器类中不使用
CompilerGenerated
属性,以确保与 .NET Compact Framework 的兼容性(感谢 reklats)。 - 已添加无参数格式化方法(感谢 Dave Apelt)。
- 改进了非字符串资源获取属性的生成。
- 2.0 - 2008 年 2 月 5 日(第二次主要发布)
- 已修复非字符串属性注释的问题(感谢 Anthony Meehan 和 Matt Rice)。
- 资源管理器初始化现在是线程安全的(感谢 Borys Byk)。
- 添加了 Visual Studio .NET 2008 兼容性。
- 1.1 - 2006 年 4 月 22 日
- 根据 Steve Hansen 的建议,改进了格式方法生成。
- 1.0 - 2006 年 4 月 18 日
- 初始发布。