是否真正缓存了资源字符串?

Moo*_*ght 2 c# resources caching winforms

不久前(当我着急时),我问了以下问题,有关使用资源字符串的性能开销,使用资源文件(.resx)时的性能开销。我得到了一个投票通过的答案,并回答了正确的答案。但是,以前,我是在错误情况下对消息字符串进行本地化,而不是对性能至关重要。现在,我被要求对我们的代码“强大”(许多性能关键代码,嵌入式循环等)实施本地化。

有一些时间来更详细地研究这个问题,我注意到调用诸如

Resources.MessageStrings.SomeResourceName
Run Code Online (Sandbox Code Playgroud)

仅将调用指向自动生成的代码MessageStrings.Designer.cs,该代码使用

internal static string SomeResourceName {
    get {
        return ResourceManager.GetString("SomeResourceName", resourceCulture);}
}
Run Code Online (Sandbox Code Playgroud)

所以,更深的挖掘,我想我会反编译ResourceManager它在发现

C:\ Program Files(x86)\参考程序集\ Microsoft \ Framework.NETFramework \ v4.5 \ mscorlib.dll

看看GetString()正在做什么[ 是否真的在缓存我的资源字符串?]。反编译,我发现

[__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;
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码中没有任何内容表明它正在“缓存”我的字符串(按单词的典型/真实含义),似乎正在执行某种类型的复杂查找。我注意到该方法正在使用undocumented __DynamicallyInvokable属性,并由Hans对这个属性进行了简短的讨论(__DynamicallyInvokable属性的作用是什么?)。

我的问题是:对于性能至关重要的代码,我可以依靠ResourceManager足够快的速度(它是否缓存我的字符串吗?),还是需要我自己预处理和缓存资源字符串?

谢谢你的时间。

Der*_*Ape 5

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

[System.Security.SecuritySafeCritical]  // auto-generated 
public virtual String GetString(String name, CultureInfo culture) {
  //...
  String value = rs.GetString(name, _ignoreCase);
  //...
}
Run Code Online (Sandbox Code Playgroud)

2。

public virtual string GetString(string name, bool ignoreCase)
{
  object objectInternal = this.GetObjectInternal(name);
  //...
}
Run Code Online (Sandbox Code Playgroud)

3。

private object GetObjectInternal(string name)
{
  //...
  Hashtable hashtable = this.Table;
  //...
  return hashtable[(object) name];
}
Run Code Online (Sandbox Code Playgroud)

因此,此时从哈希表读取值。

一旦访问资源文件,哈希表将被填充:

构造函数:

[SecuritySafeCritical]
public ResourceSet(string fileName)
{
  this.Reader = (IResourceReader) new ResourceReader(fileName);
  this.CommonInit();
  this.ReadResources();
}
Run Code Online (Sandbox Code Playgroud)

和ReadResources:

protected virtual void ReadResources()
{
  IDictionaryEnumerator enumerator = this.Reader.GetEnumerator();
  while (enumerator.MoveNext())
  {
    object obj = enumerator.Value;
    this.Table.Add(enumerator.Key, obj);
  }
}
Run Code Online (Sandbox Code Playgroud)