Are ResourceStrings Really Cached?
总之,前一段时间(当我匆忙离开时),我问了以下问题:在使用资源文件(.resx)时,使用资源字符串的性能开销。我得到了一个投赞成票的答案,认为答案是正确的。但是,以前,我对消息字符串进行了本地化,这些消息字符串是在错误条件下调用的,而不是性能关键的——现在我被要求对代码"Power House"进行本地化(许多性能关键代码、嵌入式循环等)。
有时间更详细地研究这个问题,我注意到调用一个类似
1 | Resources.MessageStrings.SomeResourceName |
只引用对自动生成的代码
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
看看
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; } } |
上面的代码中没有任何内容表明它在"缓存"我的字符串(在这个词的典型/最真实的意义上),它似乎正在进行某种类型的复杂查找。我注意到该方法使用了未记录的
我的问题是:对于性能关键的代码,我能否依赖于
谢谢你的时间。
资源被缓存。如果通过资源管理器跟踪调用堆栈,则如下所示: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); } } |
在记忆中被保存的东西就像它正在使用的
而且,像往常一样,您的分析是否告诉过您存在性能问题?尝试通过猜测可能效率低下的地方来优化代码是一个很好的主意。