从指定时间开始计时

6

我试图从数据库中获取的十进制值开始计时器,但由于Stopwatch.Elapsed.Add返回一个新的Timespan而不是修改原有Stopwatch,所以我无法找到最佳解决方案。

var offsetTimeStamp = new System.TimeSpan(0,0,0).Add(TimeSpan.FromSeconds((double)jd.ActualTime));
Stopwatch.Elapsed.Add(offsetTimeStamp);
Stopwatch.Start();
有什么想法可以实现这个吗?谢谢。

你为什么需要那个?如果你想要添加一个偏移量,那么先测量你想要测量的内容,然后将偏移量加到结果中即可... - Patryk Ćwiek
1
当“作业”被“启动”时,计时器被创建。每10秒钟,秒表当前时间将被提交到数据库。当作业暂停时,秒表停止计时。如果PC被重置(例如断电等),秒表将从内存中丢失(一个秒表列表)。因此,我需要在重新启动作业时重新创建每个秒表,从其“jd.ActualTime”恢复计时。 - dynamicuser
为什么不使用TimeSpan total = Stopwatch.Elapsed + offsetTimeStamp;或类似方式? - Nolonar
5个回答

7

普通的StopWatch不支持使用偏移时间跨度进行初始化,而TimeSpan是一个struct,因此Elapsed是不可变的。您可以编写一个StopWatch的包装器:

public class StopWatchWithOffset
{
    private Stopwatch _stopwatch = null;
    TimeSpan _offsetTimeSpan;

    public StopWatchWithOffset(TimeSpan offsetElapsedTimeSpan)
    {
        _offsetTimeSpan = offsetElapsedTimeSpan;
        _stopwatch = new Stopwatch();
    }

    public void Start()
    {
        _stopwatch.Start();
    }

    public void Stop()
    {
        _stopwatch.Stop();
    }

    public TimeSpan ElapsedTimeSpan
    {
        get
        {
            return _stopwatch.Elapsed + _offsetTimeSpan;
        }
        set
        {
            _offsetTimeSpan = value;
        }
    }
}
现在您可以添加一个起始时间段:
var offsetTimeStamp = TimeSpan.FromHours(1);
var watch = new StopWatchWithOffset(offsetTimeStamp);
watch.Start();
System.Threading.Thread.Sleep(300); 
Console.WriteLine(watch.ElapsedTimeSpan);// 01:00:00.2995983

2
ElapsedTimeSpanset 访问器中,应该是 _offsetTimeSpan = value - _stopwatch.Elapsed,以保持这三个量之间与 get 访问器相同的关系,否则如果我设置属性并立即获取它,它就没有获得我设置的值。 - Jeppe Stig Nielsen

0

这并不适用于OP的情况(我猜他们8年前就解决了),但如果你只需要为单元测试或其他非生产场景创建秒表,那么可以使用反射来修改经过的时间。

这不会给您最佳性能,并且如果Stopwatch的底层实现发生更改,则可能会出现问题,因此在使用此方法时应非常谨慎。

然而,在需要传递Stopwatch并且无法更改使用替代实现的单元测试中,我发现这种方法效果很好,风险可接受。

/// <summary>
/// Some static mechanisms for creating Stopwatch instances that start from a specific time.
/// </summary>
public static class TestStopwatch
{
    /// <summary>
    /// Creates a <see cref="Stopwatch"/> instance with a specified amount of time already elapsed
    /// </summary>
    /// <param name="start">The <see cref="TimeSpan"/> indicated the elapsed time to start from.</param>
    public static Stopwatch WithElapsed(TimeSpan start)
    {
        var sw = new Stopwatch();

        var elapsedProperty = typeof(Stopwatch).GetField("_elapsed", BindingFlags.NonPublic | BindingFlags.Instance);

        long rawElapsedTicks = start.Ticks;

        if (Stopwatch.IsHighResolution)
        {
            rawElapsedTicks = (long)((double)rawElapsedTicks / (10000000 / (double)Stopwatch.Frequency));
        }

        elapsedProperty.SetValue(sw, rawElapsedTicks);

        return sw;
    }

    /// <summary>
    /// Initializes a new <see cref="Stopwatch"/> instance, sets the elapsed time property to the specified value,
    /// and starts measuring elapsed time.
    /// </summary>
    /// <param name="start">The <see cref="TimeSpan"/> indicated the elapsed time to start from.</param>
    public static Stopwatch StartNew(TimeSpan start)
    {
        var sw = TestStopwatch.WithElapsed(start);
        sw.Start();
        return sw;
    }
}

0

我想你想在由TimeSpan指定的一定时间后开始你的Stopwatch。我不知道为什么你不想改为使用DateTime指定的时间来启动你的Stopwatch呢?

public class MyStopwatch : Stopwatch
{
    public void Start(long afterMiliseconds)
    {
        Timer t = new Timer() { Interval = 1 };
        int i = 0;
        t.Tick += (s, e) =>
        {
            if (i++ == afterMiliseconds)
            {
                Start();
                t.Stop();
            }
        };
        t.Start();
    }
}
//use it
var offsetTimeStamp = new System.TimeSpan(0,0,0).Add(TimeSpan.FromSeconds((double)jd.ActualTime));
myStopwatch.Start((long)offsetTimeStamp.TotalMiliseconds);

0

StopWatchElapsed 属性是只读的,这很合理。秒表只是测量开始和停止之间经过的时间。

如果您想要将一个时间段添加到该值中 - 请在变量中获取 Elapsed 值,并在测量后(即停止后)添加一个时间段。


0

如果您将此文件添加到项目中,则无需更改项目中的任何内容。该类继承自原始的Stopwatch类,具有相同的名称和相同的方法/属性,但具有附加功能:

  • SetOffset()方法
  • 带有偏移量的初始化

.

using System;

public class Stopwatch : System.Diagnostics.Stopwatch
{
    TimeSpan _offset = new TimeSpan();

    public Stopwatch()
    {
    }

    public Stopwatch(TimeSpan offset)
    {
        _offset = offset;
    }

    public void SetOffset(TimeSpan offsetElapsedTimeSpan)
    {
        _offset = offsetElapsedTimeSpan;
    }

    public TimeSpan Elapsed
    {
        get{ return base.Elapsed + _offset; }
        set{ _offset = value; }
    }

    public long ElapsedMilliseconds
    {
        get { return base.ElapsedMilliseconds + _offset.Milliseconds; }
    }

    public long ElapsedTicks
    {
        get { return base.ElapsedTicks + _offset.Ticks; }
    }

}

1
我不喜欢使用__new__成员来隐藏基类(如Elapsed)的成员,因为这会导致混淆。例如,如果您在编译时有一个类型为System.Diagnostics.Stopwatch的变量,在运行时它可能是YourNamespace.Stopwatch。出于这个原因,我更喜欢Tim Schmelter的答案。System.Diagnostics.Stopwatch没有声明这些属性为virtual,它并不是为此而设计的,因此从该类派生不是最佳解决方案。 - Jeppe Stig Nielsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,