我正在尝试使用. Net毛伊岛应用程序中的计时器拍照。我有图片工作得很好,可以得到5 - 10张图片的第一次运行的计时器,但不是在第二次调用计时器方法。多次运行"StartTimer"方法(在下面的代码中)会抛出"System.NullReferenceException:'Object reference not set to an instance of an object.'"在Task.Delay
调用或System.Timers.Timer
事件的后续触发上。这会导致应用程序崩溃,即使我有try...catch
块。
我也试过使用System.Threading.Timer
,但它从来没有触发。
我知道why发生了异常。算是吧即使我没有包含CancellationToken
或者我创建了一个新的token,它仍然会抛出错误。
我使用Camera.Maui NuGet插件的相机功能,如果这件事。如果我能让AutoSnapShot
功能工作,我可能不需要在这里得到答案,但那就是a different question。
这是一个针对Android API 32的. Net 7 Maui应用程序。到目前为止,我只在Android中测试过它,但我已经在运行API版本28、31和33的真实设备上在Release
和Debug
模式下测试过它。唯一的区别是API 28电话在第二次运行时拍摄1张照片,然后抛出异常,然后拍摄另一张照片。此外,异常不会使应用程序崩溃。这允许我尝试多次运行该方法,但每次都有与第二次运行相同的问题。
那么,我做错了什么和/或我如何避免NullReferenceException
?
代码
问题代码在它自己的类中。我从MainPage.xaml.cs
中的异步方法调用它。即使我为“timer”变量创建了一个新的类示例,它仍然会抛出NullReferenceException
。我还更改了类以实现IDisposable
,以尝试清除任何残留的...不管是什么可能影响类或方法的重用,它仍然会抛出异常。
编辑:我已经做了一些更多的测试,在实现IDisposable
和不实现IDisposable
的情况下,“timer”变量是本地的,并且在实现IDisposable
时不使用using
语句,并且当多次运行“StartTimer”时,所有测试仍然抛出NullReferenceException
。
private readonly TimerTrigger timer = new();
private async Task<bool> StartCapture()
{
try
{
...
/*
using TimerTrigger timer = new();
*/
timer.StartTimer(quantity, timerDelay);
...
}
catch
{
return false;
}
return true;
}
版本1,带有Task.Delay
:
public async void StartTimer(int quantity, int timerDelay)
{
try
{
CancellationTokenSource wtoken = new();
for (int i = 0; i < quantity; i++)
{
if (i > 0)
{
try
{
// It doesn't matter if I use a token or not, the exception is thrown
// await Task.Delay(timerDelay);
// await Task.Delay(timerDelay, CancellationToken.None);
await Task.Delay(timerDelay, wtoken.Token);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message); // This is only hit in API 28
}
}
// Take picture
}
}
catch (Exception e)
{
Console.WriteLine(e.Message); // This is never hit
}
}
版本2,带有System.Timers.Timer
:
private System.Timers.Timer aTimer;
private int totalQuantity = 0;
private int totalTaken = 0;
public void StartTimer(int quantity, int timerDelay)
{
totalQuantity = quantity;
aTimer = new()
{
Interval = timerDelay
};
aTimer.Elapsed += SnapPic;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private void SnapPic(object sender, ElapsedEventArgs e)
{
try
{
// Take picture
totalTaken++;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message); // This is never hit
EndCapture(false);
}
EndCapture(true);
}
private void EndCapture(bool success)
{
if (!success || totalTaken >= totalQuantity)
{
aTimer.AutoReset = false;
aTimer.Enabled = false;
}
}
版本3,使用System.Threading.Timer
:
private int totalQuantity = 0;
private int totalTaken = 0;
public void StartTimer(int quantity, int timerDelay)
{
AutoResetEvent autoEvent = new(true);
totalQuantity = quantity;
using Timer bTimer = new(SnapPic, autoEvent, 0, timerDelay);
// using Timer bTimer = new(SnapPic, null, 0, timerDelay); // This doesn't run, either
}
private void SnapPic(object sender)
{
// This method is apparently never run
try
{
// Take picture
totalTaken++;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
EndCapture(false); // Reusing this method from Version 2
}
EndCapture(true); // Reusing this method from Version 2
}
1条答案
按热度按时间smdncfj31#
在阅读了更多关于计时器的内容并使用了另一个使用
System.Timers.Timer
的项目之后,我找到了一个解决方案。在版本2中,我需要调用
Start
方法,而不是设置属性来启动计时器。我需要调用Close
和Dispose
方法,而不是设置属性来结束计时器。旧的和破碎的:
新的和工作:
我必须加上“totalTaken = 0;”到“EndCapture”方法,如果有人关心的话。不重置这个变量使我认为当把“timer”作为“MainPage”类的属性放回去时,问题仍然会发生。
现在我只需要重命名“aTimer”变量。我已经剥离了一堆不工作的测试代码和版本1和3。