c#代码似乎以无效方式进行优化,使得对象值变为null
我有以下代码,表现出一个奇怪的问题:
var all = new FeatureService().FindAll(); System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null"); System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException
FindAll方法的签名是:
public List FindAll()
单步执行代码我已确认FindAll的返回值不为null,正如您从Assert中看到的那样,“all”变量不为null,但在下一行中它似乎为null。
调用ToString()方法时,问题不是特定于失败。 在尝试跟踪根本原因时,我将其简化为可重现的示例。
这可能是一个线索:在调试器中,变量“all”出现在Locals窗口中,其值为“无法获取本地或参数的值’all’,因为它在此指令指针处不可用,可能是因为它已经被优化了。“
我考虑尝试其他地方记录的方法之一来禁用代码优化,但这并不能真正解决问题,因为代码的发布版本仍然会得到优化。
我正在使用Visual Studio 2010与.NET 4.0。
有什么想法吗?
更新:根据请求,这是整个方法:
protected override List GetModels() { var all = new FeatureService().FindAll(); var wr = new WeakReference(all); System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null"); System.Diagnostics.Debug.WriteLine(wr.IsAlive); System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException return all; }
作为一个仅供参考,原始实施只是:
protected override List GetModels() { return new FeatureService().FindAll(); }
我最初在调用方法中遇到了nullexception。 我发布的代码是在跟踪问题一段时间之后。
更新#2:根据要求,这是来自exception的堆栈跟踪:
at FeatureCrowd.DomainModel.FeatureSearch.GetModels() in C:UsersGaryDocumentsVisual Studio 2010ProjectsFeatureCrowdFeatureCrowd.DomainModelFeatureSearch.cs:line 32 at FeatureCrowd.DomainModel.FeatureSearch.CreateIndex() in C:UsersGaryDocumentsVisual Studio 2010ProjectsFeatureCrowdFeatureCrowd.DomainModelFeatureSearch.cs:line 42 at FeatureCrowd.DomainModel.FeatureService.CreateSearchIndex() in C:UsersGaryDocumentsVisual Studio 2010ProjectsFeatureCrowdFeatureCrowd.DomainModelFeatureService.cs:line 100 at Website.MvcApplication.BuildLuceneIndexThread(Object sender) in C:UsersGaryDocumentsVisual Studio 2010ProjectsFeatureCrowdFeatureCrowd.WebsiteGlobal.asax.cs:line 50 at Website.MvcApplication.Application_Start() in C:UsersGaryDocumentsVisual Studio 2010ProjectsFeatureCrowdFeatureCrowd.WebsiteGlobal.asax.cs:line 61
在通过TeamViewer查看代码,最后在我自己的机器上下载,编译和运行代码之后,我相信这是C#4.0中编译器错误的情况。
在管理将问题简化为一些简单的项目和文件后,我发布了一个带有validation请求的问题。 它在这里可用: 可能的C#4.0编译器错误,其他人可以validation吗?
可能的罪魁祸首不是这种方法:
protected override List GetModels() { var fs = new FeatureService(); var all = fs.FindAll(); var wr = new WeakReference(all); System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null"); System.Diagnostics.Debug.WriteLine(wr.IsAlive); System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException return all; }
但是它调用的方法是FeatureService.FindAll:
public List FindAll() { string key = Cache.GetQueryKey("FindAll"); var value = Cache.Load>(key); if (value == null) { var query = Context.Features; value = query.ToList().Select(x => Map(x)).ToList(); var policy = Cache.GetDefaultCacheItemPolicy(value.Select(x => Cache.GetObjectKey(x.Id.ToString())), true); Cache.Store(key, value, policy); } value = new List (); return value; }
如果我改变了GetModels中的调用:
var all = fs.FindAll();
对此:
var all = fs.FindAll().ToList(); // remember, it already returned a list
然后程序崩溃了ExecutionEngineException
。
在进行了清理,构建,然后通过Reflector查看编译后的代码之后,输出看起来如何(滚动到重要部分的代码底部):
public List FindAll() { List value; Func CS$<>9__CachedAnonymousMethodDelegate6 = null; List CS$<>9__CachedAnonymousMethodDelegate7 = null; string key = base.Cache.GetQueryKey("FindAll"); if (base.Cache.Load>(key) == null) { if (CS$<>9__CachedAnonymousMethodDelegate6 == null) { CS$<>9__CachedAnonymousMethodDelegate6 = (Func) delegate (Feature x) { return this.Map(x); }; } value = base.Context.Features.ToList().Select(((Func) CS$<>9__CachedAnonymousMethodDelegate6)).ToList(); if (CS$<>9__CachedAnonymousMethodDelegate7 == null) { CS$<>9__CachedAnonymousMethodDelegate7 = (List ) delegate (FeatureModel x) { return base.Cache.GetObjectKey(x.Id.ToString()); }; } Func policy = (Func) base.Cache.GetDefaultCacheItemPolicy(value.Select((Func) CS$<>9__CachedAnonymousMethodDelegate7), true); base.Cache.Store>(key, value, (CacheItemPolicy) policy); } value = new List (); bool CS$1$0000 = (bool) value; return (List ) CS$1$0000; }
注意方法的最后3行,这是它们在代码中的样子:
value = new List(); return value;
这是Reflector所说的:
value = new List(); bool CS$1$0000 = (bool) value; return (List ) CS$1$0000;
它创建列表,然后将其转换为布尔值,然后将其转换回列表并返回它。 最有可能这会导致堆栈问题。
这是IL中的相同方法(仍然通过Reflector),我已经剥离了大部分代码:
.method public hidebysig instance class [mscorlib]System.Collections.Generic.List`1 FindAll() cil managed { .maxstack 5 .locals init ( [0] string key, [1] class [mscorlib]System.Collections.Generic.List`1 'value', [2] class [System.Data.Entity]System.Data.Objects.ObjectSet`1 query, [3] class [mscorlib]System.Func`2 policy, [4] class [mscorlib]System.Func`2 CS$<>9__CachedAnonymousMethodDelegate6, [5] class [mscorlib]System.Collections.Generic.List`1 CS$<>9__CachedAnonymousMethodDelegate7, [6] bool CS$1$0000, [7] char CS$4$0001) ... L_009f: newobj instance void [mscorlib]System.Collections.Generic.List`1 ::.ctor() L_00a4: stloc.1 L_00a5: ldloc.1 L_00a6: stloc.s CS$1$0000 L_00a8: br.s L_00aa L_00aa: ldloc.s CS$1$0000 L_00ac: ret }
这是一个显示调试会话的截屏video ,如果你只想要reflection器输出,请跳到大约2:50。
在Lasse发现FindAll方法生成了错误的IL之后,我又遇到了另一种也产生错误IL的方法 – 我也找到了根本原因和解决方案。
第二种方法中的相关行是:
var policy = Cache.GetDefaultCacheItemPolicy(dependentKeys, true);
缓存是我自己的对象。 GetDefaultCacheItemPolicy方法返回System.Runtime.Caching.CacheItemPolicy对象。 但是,生成的IL看起来像这样:
Func policy = (Func) base.Cache.GetDefaultCacheItemPolicy(dependentKeys, true);
这里有两个项目。 生成错误IL的方法在一个名为DomainModel的项目中,而Cache对象在Utilities项目中,由第一个项目引用。 第二个项目包含对System.Runtime.Caching的引用,但第一个项目没有。
修复是将System.Runtime.Caching的引用添加到第一个项目。 现在生成的IL看起来正确:
CacheItemPolicy policy = base.Cache.GetDefaultCacheItemPolicy(dependentKeys, true);
第一种方法(Lasse在他的回答中发布的)现在也产生了适当的IL。
万岁!
留给子孙后代,这不是问题。
看到我的新答案 。
这就是我所相信的。
与你所说的相反,我认为该程序实际上并没有在发布的任何行中崩溃,而是在它们之后的其中一行上崩溃,而你没有发布。
我相信这是因为我也相信你正在进行Release-build,在这种情况下,两个Debug行都将被删除,因为它们被标记为[Conditional("DEBUG")]
属性。
这里的线索是all
变量已被优化掉,这应该只在Release-build期间发生,而不是Debug-build。
换句话说,我相信all
变量实际上是null
,并且Debug行没有被执行,因为它们没有被编译到程序集中。 调试器尽职尽责地报告all
变量不再存在。
请注意,所有这些都应该易于测试。 只需在您发布的两个Debug行中的第一个上放置一个断点即可。 如果断点被击中,我的假设很可能是错误的。 如果没有(并且我将猜测断点符号在运行时显示为空心圆),那么这些行不会被编译到程序集中。
如果您怀疑变量以某种方式被优化掉了,您可以使用WeakReference
的IsAlive
属性通过all
对象或通过GC.KeepAlive(all)
。 我不确定这是否有用,但值得一试。
另一种可能性,虽然极不可能,但由于某种原因, List
会抛出该exception。 您可以使用.NET Reflector等工具validation这一点,以查看该方法的确切function。
上述就是C#学习教程:c#代码似乎以无效方式进行优化,使得对象值变为null分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1008376.html