C#中如何强制传入资源文件项的键而非值?
如何强制传入资源键而非资源值到ResourceString类?
为实现运行时获取多语言字符串,我编写了如下ResourceString类,可通过GetValue()方法获取对应语言的字符串:
internal class ResourceString : IResourceString { public ResourceString(string id, ResourceManager rm, params IResourceString[] parameters) { Id = id; Rm = rm; m_Parameters = parameters ?? Array.Empty<IResourceString>(); } public string Id { get; } private ResourceManager Rm { get; } private readonly IResourceString[] m_Parameters; public string GetValue(CultureInfo cultureInfo) { if (m_Parameters.Length > 0) { return string.Format(Rm.GetString(Id, cultureInfo), m_Parameters.Select(p => p.GetValue(cultureInfo))); } else { return Rm.GetString(Id, cultureInfo); } } }; // 下游获取特定值的扩展方法 internal static class MlExtension { public static string GetValue(this IResourceString source) { return source.GetValue(CultureInfo.CurrentCulture); } }
问题在于创建ResourceString实例时,需传入资源文件的键(用nameof()获取)作为id参数,但开发者容易误传资源值:
- 正确写法:
new ResourceString(nameof(Guid.Rule_Name), Guid.ResourceManager) - 错误写法:
new ResourceString(Guid.Rule_Name, Guid.ResourceManager)
以下是几种实用的解决方式:
方案1:用表达式树自动解析资源键与ResourceManager
修改构造逻辑,通过表达式树绑定资源属性,自动提取键和对应的ResourceManager,从根源上避免手动传参错误:
public static class ResourceStringFactory { public static ResourceString Create(Expression<Func<string>> resourcePropertyExpr, params IResourceString[] parameters) { if (!(resourcePropertyExpr.Body is MemberExpression memberExpr)) { throw new ArgumentException("必须传入资源类的静态属性表达式,例如 () => Resources.MyString"); } // 自动获取资源键(属性名) string resourceKey = memberExpr.Member.Name; // 自动获取资源类的ResourceManager var resourceType = memberExpr.Member.DeclaringType; var resourceManagerProp = resourceType.GetProperty("ResourceManager", BindingFlags.Public | BindingFlags.Static); if (resourceManagerProp == null) { throw new InvalidOperationException($"资源类 {resourceType.Name} 未生成公开的静态ResourceManager属性"); } var resourceManager = (ResourceManager)resourceManagerProp.GetValue(null); return new ResourceString(resourceKey, resourceManager, parameters); } }
调用方式:
// 只能通过表达式传入资源属性,自动解析所有必要参数,杜绝传错 var resourceStr = ResourceStringFactory.Create(() => Guid.Rule_Name);
方案2:编译时检查(Roslyn分析器)
编写轻量Roslyn分析器,在编译阶段直接识别错误传参:
- 定位
ResourceString构造函数的调用 - 检查第一个参数,如果是资源类静态属性的直接引用(而非
nameof()表达式),就抛出编译警告或错误
这种方式能在开发阶段即时提示错误,无需等到运行时才发现问题。
方案3:用强类型包装资源键
创建仅允许通过合法方式生成的ResourceKey结构体,修改ResourceString构造函数接受该类型而非原始字符串:
public readonly struct ResourceKey { public string Value { get; } private ResourceKey(string value) => Value = value; // 仅允许通过属性表达式生成,确保是资源键 public static ResourceKey From<T>(Expression<Func<T>> propertyExpr) { if (!(propertyExpr.Body is MemberExpression memberExpr)) { throw new ArgumentException("必须传入属性表达式"); } return new ResourceKey(memberExpr.Member.Name); } } // 修改ResourceString构造函数 public ResourceString(ResourceKey id, ResourceManager rm, params IResourceString[] parameters) { Id = id.Value; Rm = rm; m_Parameters = parameters ?? Array.Empty<IResourceString>(); }
调用方式:
// 必须通过From方法生成合法的ResourceKey var key = ResourceKey.From(() => Guid.Rule_Name); var resourceStr = new ResourceString(key, Guid.ResourceManager);
这种方式至少能确保传入的id是资源键而非资源值。
内容的提问来源于stack exchange,提问作者tickietackie
相关产品推荐
相关产品推荐

