You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:55:29