关于c#:ResourceStrings真的被缓存了吗?

Are ResourceStrings Really Cached?

总之,前一段时间(当我匆忙离开时),我问了以下问题:在使用资源文件(.resx)时,使用资源字符串的性能开销。我得到了一个投赞成票的答案,认为答案是正确的。但是,以前,我对消息字符串进行了本地化,这些消息字符串是在错误条件下调用的,而不是性能关键的——现在我被要求对代码"Power House"进行本地化(许多性能关键代码、嵌入式循环等)。

有时间更详细地研究这个问题,我注意到调用一个类似

1
Resources.MessageStrings.SomeResourceName

只引用对自动生成的代码MessageStrings.Designer.cs的调用,它使用

1
2
3
4
internal static string SomeResourceName {
    get {
        return ResourceManager.GetString("SomeResourceName", resourceCulture);}
}

因此,深入挖掘,我想我会分解在

C:\Program Files (x86)
eference Assemblies\Microsoft\Framework.NETFramework\v4.5\mscorlib.dll

看看GetString()在做什么[它真的在缓存我的资源字符串吗?]分解,我发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
[__DynamicallyInvokable]
public virtual string GetString(string name, CultureInfo culture)
{
  if (name == null)
    throw new ArgumentNullException("name");
  if (ResourceManager.s_IsAppXModel && object.ReferenceEquals((object) culture, (object) CultureInfo.CurrentUICulture))
    culture = (CultureInfo) null;
  if (this._bUsingModernResourceManagement)
  {
    if (this._PRIonAppXInitialized)
      return this.GetStringFromPRI(name, culture == null ? (string) null : culture.Name, this._neutralResourcesCulture.Name);
    if (this._PRIExceptionInfo == null || this._PRIExceptionInfo._PackageSimpleName == null || this._PRIExceptionInfo._ResWFile == null)
      throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoPRIresources"));
    throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_ResWFileNotLoaded", (object) this._PRIExceptionInfo._ResWFile, (object) this._PRIExceptionInfo._PackageSimpleName));
  }
  else
  {
    if (culture == null)
      culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
    if (FrameworkEventSource.IsInitialized)
      FrameworkEventSource.Log.ResourceManagerLookupStarted(this.BaseNameField, this.MainAssembly, culture.Name);
    ResourceSet resourceSet1 = this.GetFirstResourceSet(culture);
    if (resourceSet1 != null)
    {
      string @string = resourceSet1.GetString(name, this._ignoreCase);
      if (@string != null)
        return @string;
    }
    foreach (CultureInfo culture1 in new ResourceFallbackManager(culture, this._neutralResourcesCulture, true))
    {
      ResourceSet resourceSet2 = this.InternalGetResourceSet(culture1, true, true);
      if (resourceSet2 != null)
      {
        if (resourceSet2 != resourceSet1)
        {
          string @string = resourceSet2.GetString(name, this._ignoreCase);
          if (@string != null)
          {
            if (this._lastUsedResourceCache != null)
            {
              lock (this._lastUsedResourceCache)
              {
                this._lastUsedResourceCache.lastCultureName = culture1.Name;
                this._lastUsedResourceCache.lastResourceSet = resourceSet2;
              }
            }
            return @string;
          }
          else
            resourceSet1 = resourceSet2;
        }
      }
      else
        break;
    }
    if (FrameworkEventSource.IsInitialized)
      FrameworkEventSource.Log.ResourceManagerLookupFailed(this.BaseNameField, this.MainAssembly, culture.Name);
    return (string) null;
  }
}

上面的代码中没有任何内容表明它在"缓存"我的字符串(在这个词的典型/最真实的意义上),它似乎正在进行某种类型的复杂查找。我注意到该方法使用了未记录的__DynamicallyInvokable属性,并发现了Hans对该属性的breaf讨论(什么是"dynamicallyinvocable"属性?).

我的问题是:对于性能关键的代码,我能否依赖于ResourceManager足够快(它是否缓存了我的字符串?)还是需要自己预处理和缓存资源字符串?

谢谢你的时间。


资源被缓存。如果通过资源管理器跟踪调用堆栈,则如下所示:1。

1
2
3
4
5
6
[System.Security.SecuritySafeCritical]  // auto-generated
public virtual String GetString(String name, CultureInfo culture) {
  //...
  String value = rs.GetString(name, _ignoreCase);
  //...
}

2。

1
2
3
4
5
public virtual string GetString(string name, bool ignoreCase)
{
  object objectInternal = this.GetObjectInternal(name);
  //...
}

三。

1
2
3
4
5
6
7
private object GetObjectInternal(string name)
{
  //...
  Hashtable hashtable = this.Table;
  //...
  return hashtable[(object) name];
}

所以此时,值是从哈希表中读取的。

访问资源文件后,将填充哈希表:

Constructor:

1
2
3
4
5
6
7
[SecuritySafeCritical]
public ResourceSet(string fileName)
{
  this.Reader = (IResourceReader) new ResourceReader(fileName);
  this.CommonInit();
  this.ReadResources();
}

阅读资源:

1
2
3
4
5
6
7
8
9
protected virtual void ReadResources()
{
  IDictionaryEnumerator enumerator = this.Reader.GetEnumerator();
  while (enumerator.MoveNext())
  {
    object obj = enumerator.Value;
    this.Table.Add(enumerator.Key, obj);
  }
}


在记忆中被保存的东西就像它正在使用的ResourceSet,那些看起来直接保存着字符串。您不必在这个方法中看到它,因为此时,对于不同的资源名和不同的文化,每个调用都可能出现。

而且,像往常一样,您的分析是否告诉过您存在性能问题?尝试通过猜测可能效率低下的地方来优化代码是一个很好的主意。