【问题标题】:System.ArgumentNullException in ResourceManager.GetString internalsResourceManager.GetString 内部中的 System.ArgumentNullException
【发布时间】:2010-12-01 10:12:10
【问题描述】:

我的代码:

System.Resources.ResourceManager resourceManager = GetResourceManager();
string str = resourceManager.GetString("delete", new CultureInfo(1033));

在 .NET 2.0 下编译的当前项目中,一切正常。变量 str 包含 LCID 1033 的资源字符串 - 删除,没关系。

我们现在正在升级到 .NET 4.0,在目标框架 .NET 4.0 下重新编译项目。 现在编译为 .NET 4.0 程序集,它会抛出异常 System.ArgumentNullException 并带有消息 Value cannot be null.。堆栈跟踪:

   at System.Threading.Monitor.Enter(Object obj)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo requestedCulture, Boolean createIfNotExists, Boolean tryParents, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)
   at System.Resources.ResourceManager.GetString(String name, CultureInfo culture)

这里有趣的是 stacktrace,它指向 ResourceManager.InternalGetResourceSet 中的内部框架方法,这会导致使用 null 对象调用 Monitor.Enter。但是我用非空参数调用方法 GetString GetString("delete", new CultureInfo(1033))

这个错误似乎类似于System.ArgumentNullException in System.Threading.Monitor.Enter。可能是 Monitor.Enter 中的一些错误,还是其他什么?

更新: 当我在调试器中查看对象resourceManager.ResourceSets.Items[2].Value.Table["delete"] 时,它包含字符串值“删除”。此处的属性 Items[2] 指向 LCID 1033。这意味着资源管理器已经包含语言 1033 中资源键 delete 的本地化字符串。有谁知道哪里出错了?

【问题讨论】:

  • 非常有趣。我试图探索 Reflector 中的 ResourceManager 类,但我只有 mscorlib 2.0。如果它是一个错误,我会在 ResourceManager.InternalGetResourceSet 方法中搜索它。您使用的是 .NET 还是 Mono?我想帮忙
  • 我在 windows 上使用 .NET,FW 4.0,刚刚用 Reflector 反编译,但在内部方法中找不到任何异常的东西。在这里看看这个反编译的方法 - paste.org/pastebin/view/25636.
  • 现在我在 Visual Studio 中使用 .NET Reflector 插件来调试 mscorlib,并发现它位于第 14 行 - lock (localResourceSets)(在粘贴的示例上)

标签: .net c#-4.0 locking resourcebundle


【解决方案1】:

我还认为您应该声明并使用自己的字典进行存储,因为 ResourceSets 在 .NET 4.0 下被标记为已过时

【讨论】:

    【解决方案2】:

    我自己找到了答案。以下是详细信息: 我们有这样的 ResourceManager 的自定义实现:

    public class DatabaseResourceManager : System.Resources.ResourceManager 
    {
      public DatabaseResourceManager(int applicationID, string bundle) 
      {
        foreach (int languageID in ResourceProvider.Provider.GetLanguages(applicationID))
        {
          DatabaseResourceReader r = new DatabaseResourceReader(applicationID, bundle, languageID);
          ResourceSets.Add(new CultureInfo(languageID), new ResourceSet(r));
        }
    }
    

    在 .NET 2.0 中它运行良好,但在 .NET 4.0 中,ResourceManager 的实现发生了一些变化。我认为问题出在 .NET 2.0 中实例化私有字段 this._resourceSets 的无参数构造函数中(稍后在 InternalGetResourceSet 中用于 Monitor.Enter)。但在 .NET 4.0 中,无参数构造函数不会实例化私有字段 this._resourceSets,因此稍后会失败(如上文所述)。

    我必须重写我的自定义资源管理器才能解决问题:

    public class DatabaseResourceManager : System.Resources.ResourceManager 
    {
      public DatabaseResourceManager(int applicationID, string bundle) 
      {
        ResourceSets = new Hashtable();
        this.applicationID = applicationID;
        this.bundle = bundle;
      }
    
      protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
      {
        if (this.ResourceSets.Contains(culture.Name))
            return this.ResourceSets[culture.Name] as ResourceSet;
    
        lock (syncLock)
        {
            if (this.ResourceSets.Contains(culture.Name))
                return this.ResourceSets[culture.Name] as ResourceSet;
    
            DatabaseResourceReader r = new DatabaseResourceReader(applicationID, bundle, culture.LCID);
            ResourceSet rs = new ResourceSet(r);
    
            this.ResourceSets.Add(culture.Name, rs);
    
            return rs;
        }
      }
    }
    

    这里的“魔术”是我必须覆盖方法 InternalGetResourceSet 以从自定义存储 (db) 加载我的资源并返回指定区域性的“ResourceSet”。现在它就像一个魅力。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-26
      • 2016-07-10
      • 1970-01-01
      相关资源
      最近更新 更多