我正在为Windows Phone 8.1(Silverlight)开发C#应用程序。最近我遇到了与应用程序入睡和故事板相关的问题。
构造如下:
class X : DependencyObject
{
public static readonly DependencyProperty vProperty =
DependencyProperty.Register("v", typeof(double), typeof(X), new PropertyMetadata(0.0));
public double v
{
get
{
return (double)GetValue(vProperty);
}
set
{
SetValue(vProperty, value);
}
}
private Storyboard _storyboard;
void Prepare()
{
_storyboard = new Storyboard();
var animation= new DoubleAnimation
{
From = 0,
To = 1,
BeginTime = 0,
Duration = 0,
};
_storyboard.Children.Add(animation);
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, vProperty);
}
void Go()
{
_storyboard.Begin();
}
}
如果应用程序放在“Prepare”和“Go”之间的背景中(大约10%的再现率),则会从_storyboard.Begin()内部抛出NullReferenceException。当然最终会崩溃。
我无法确定问题来源,因为我需要quickfix,因此我决定在这种罕见的情况下捕获这个NullRefereneceException。这是真正的问题开始的地方。我已将“Go”实现更改为:
public void Go()
{
Debug.WriteLine("BreakPoint 1");
try
{
_storyboard.Begin();
}
catch (NullReferenceException)
{
Debug.WriteLine("BreakPoint 2");
}
}
之后崩溃完全无法再现,但问题是“BreakPoint 2”永远不会被击中(输出中也没有打印输出)。 “BreakPoint 1”通常也会被点击和打印。将NullReferenceException更改为其他异常类型(不是父类型ofc)会导致重新出现崩溃。
所以...这里发生了什么?这个崩溃是否被缓存?这有什么奇怪的行为?假设它能按预期工作是否安全?
其他问题:也许您知道为什么原始代码首先崩溃了?
编辑: TargetInvocationExceptions的internalException堆栈跟踪如下所示:
at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
at MS.Internal.XcpImports.Storyboard_Begin(Storyboard storyboard)
at System.Windows.Media.Animation.Storyboard.Begin()
at X.Go()
答案 0 :(得分:4)
我知道您已经说过您尝试使用父类型进行NullReferenceException,但请尝试在调试器中运行以下 :
public void Go()
{
Debug.WriteLine("BreakPoint 1");
try
{
_storyboard.Begin();
}
catch (Exception)
{
Debug.WriteLine("BreakPoint 2");
System.Diagnostics.Debugger.Break();
}
}
我怀疑是因为你是在调试器中运行而没有触发。如果System.Diagnostics.Debugger.Launch();
不起作用,也请在catch中尝试.Break();
。如果throw;
不起作用,最后还要在catch中尝试.Launch();
。
如果调试器试图在任何一种情况下启动,那么你还有另一条线索。
<强>更新强>:
我无法告诉您可能发生这种情况的所有原因,因为可能无法准确确定在您的情况下造成这种情况的原因。
由于使用了多线程,我看到过这样的行为。在没有附加调试器的情况下运行时,多线程的行为会有所不同。时序问题和竞争条件可以防止在调试器中抛出异常,否则在没有连接调试器时可能会频繁发生异常。
我还遇到过在第三方代码中使用System.Diagnostics.Debugger.IsAttached
甚至我的团队代码的情况,这些代码导致应用程序基于使用此检查的if
语句而表现不同。
最后,我有时无法想出行为发生的具体原因。每当我看到根据是否附加调试器而表现出不同的行为时,我就学会了使用System.Diagnostics.Debugger.Break()
方法。有时它真的只是一种直觉。