【问题标题】:Start a stopwatch from specified time从指定时间开始秒表
【发布时间】:2013-07-16 09:26:40
【问题描述】:

我试图从给定时间开始秒表(从数据库中提取的十进制值)。但是,因为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();

有什么想法可以做到这一点吗?干杯

【问题讨论】:

  • 你为什么需要那个?如果要添加偏移量,则测量要测量的内容,然后将偏移量添加到结果中...
  • 计时器在“作业”“开始”时创建。每 10 秒,秒表当前时间被提交到数据库。当作业暂停时,秒表停止滴答作响。如果 PC 被重置 IE 断电等,秒表会从内存中丢失(秒表列表)。因此,我需要在重新启动作业时重新创建每个秒表,从而从其“jd.ActualTime”恢复计时。
  • 为什么不用TimeSpan total = Stopwatch.Elapsed + offsetTimeStamp; 或类似的?

标签: c# winforms time stopwatch


【解决方案1】:

普通的StopWatch 不支持带有偏移时间跨度的初始化,而TimeSpanstruct,因此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

【讨论】:

  • ElapsedTimeSpanset 访问器中,不应该是_offsetTimeSpan = value - _stopwatch.Elapsed 在这三个量级之间保持与get 访问器相同的关系吗?否则,如果我设置该属性并在之后立即获取它,它还没有获得我设置它的值。
【解决方案2】:

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

如果您想为值添加时间跨度 - 在变量中获取 Elapsed 值并在您测量后(即停止后)为其添加时间跨度。

【讨论】:

    【解决方案3】:

    我认为您想在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);
    

    【讨论】:

      【解决方案4】:

      这不太适合 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;
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果您将此文件添加到您的项目中,则您的项目中无需进行任何更改。该类继承自原始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; }
            }
        
        }
        

        【讨论】:

        • 隐藏基类成员(如Elapsed)与new同名成员的做法,我不喜欢.这令人困惑,例如如果你有一个编译时类型的变量System.Diagnostics.Stopwatch,它在运行时恰好是YourNamespace.Stopwatch。出于这个原因,我更喜欢 Tim Schmelter 的回答。 System.Diagnostics.Stopwatch 没有声明这些属性 virtual,它不是为它设计的,因此从该类派生不是最好的解决方案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-07
        • 2021-11-29
        • 2023-03-15
        • 1970-01-01
        • 2021-05-09
        相关资源
        最近更新 更多