C# 试图用秒表包装一个函数
C# Trying to wrap a function with a stopwatch
我一直在尝试查看函数在我的代码中执行需要多长时间,以了解我可以优化的地方。现在我使用一个助手 class,它本质上是一个秒表,带有一条消息来检查这些。这样做的目的是我应该能够在助手中包装我想要的任何方法调用,并且我会得到它的持续时间。
public class StopwatcherData
{
public long Time { get; set; }
public string Message { get; set; }
public StopwatcherData(long time, string message)
{
Time = time;
Message = message;
}
}
public class Stopwatcher
{
public delegate void CompletedCallBack(string result);
public static List<StopwatcherData> Data { get; set; }
private static Stopwatch stopwatch { get; set;}
public Stopwatcher()
{
Data = new List<StopwatcherData>();
stopwatch = new Stopwatch();
stopwatch.Start();
}
public static void Click(string message)
{
Data.Add(new StopwatcherData(stopwatch.ElapsedMilliseconds, message));
}
public static void Reset()
{
stopwatch.Reset();
stopwatch.Start();
}
}
现在要使用这个,我必须在我想要的功能之前调用重置,以便重新启动计时器,然后在它之后调用点击。
Stopwatcher.Reset()
MyFunction();
Stopwatcher.Click("MyFunction");
我已经阅读了一些有关委托和操作的内容,但我不确定如何将它们应用于这种情况。理想情况下,我会将函数作为 Stopwatcher 调用的一部分传递。
//End Goal:
Stopwatcher.Track(MyFunction(), "MyFunction Time");
欢迎任何帮助。
是的,您可以创建一个计时器函数来接受任何操作作为委托。试试这个区块:
public static long TimeAction(Action action)
{
var timer = new Stopwatch();
timer.Start();
action();
timer.Stop();
return timer.ElapsedMilliseconds;
}
可以这样使用:
var elapsedMilliseconds = TimeAction(() => MyFunc(param1, param2));
如果你的包装函数 returns 是一个值,这会有点尴尬,但你可以通过从闭包中分配一个变量来处理这个问题,如下所示:
bool isSuccess ;
var elapsedMilliseconds = TimeToAction(() => {
isSuccess = MyFunc(param1, param2);
});
这样分析你的应用程序并不是一个好主意,但如果你坚持,你至少可以做一些改进。
首先,不要重复使用 Stopwatch
,只需在每次需要时创建新的即可。
其次,您需要处理两种情况 - 一种是委托您传递了 return 的值,另一种是它没有。
由于您的 Track
方法是静态的 - 使其线程安全是一种常见的做法。非线程安全的静态方法是非常糟糕的主意。为此,您可以将消息存储在线程安全的集合中,例如 ConcurrentBag
,或者每次将项目添加到列表时都使用 lock
。
最后你可以得到这样的结果:
public class Stopwatcher {
private static readonly ConcurrentBag<StopwatcherData> _data = new ConcurrentBag<StopwatcherData>();
public static void Track(Action action, string message) {
var w = Stopwatch.StartNew();
try {
action();
}
finally {
w.Stop();
_data.Add(new StopwatcherData(w.ElapsedMilliseconds, message));
}
}
public static T Track<T>(Func<T> func, string message) {
var w = Stopwatch.StartNew();
try {
return func();
}
finally {
w.Stop();
_data.Add(new StopwatcherData(w.ElapsedMilliseconds, message));
}
}
}
并像这样使用它:
Stopwatcher.Track(() => SomeAction(param1), "test");
bool result = Stopwatcher.Track(() => SomeFunc(param2), "test");
如果您要将其与异步委托(return Task
或 Task<T>
)一起使用 - 您需要为这种情况再添加两个重载。
我前段时间也遇到过这个问题,一直担心当我将 Stopwatcher.Track(() => SomeFunc(), "test")
(参见 Evk 的回答)改回 SomeFunc()
时会留下错误。所以我想办法在不改变它的情况下包装它!
我想到了一个用法,这肯定不是预期的目的。
public class OneTimeStopwatch : IDisposable
{
private string _logPath = "C:\Temp\OneTimeStopwatch.log";
private readonly string _itemname;
private System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
public OneTimeStopwatch(string itemname)
{
_itemname = itemname;
sw.Start();
}
public void Dispose()
{
sw.Stop();
System.IO.File.AppendAllText(_logPath, string.Format($"{_itemname}: {sw.ElapsedMilliseconds}ms{Environment.NewLine}"));
}
}
这个方法很简单
using (new OneTimeStopwatch("test"))
{
//some sensible code not to touch
System.Threading.Thread.Sleep(1000);
}
//logfile with line "test: 1000ms"
我只需要删除 2 行(和自动格式)即可使其恢复正常。
另外,我可以在这里轻松地换行多行,如果不在其他方法中定义新函数,这是不可能的。
同样,对于几毫秒的术语,不建议这样做。
我一直在尝试查看函数在我的代码中执行需要多长时间,以了解我可以优化的地方。现在我使用一个助手 class,它本质上是一个秒表,带有一条消息来检查这些。这样做的目的是我应该能够在助手中包装我想要的任何方法调用,并且我会得到它的持续时间。
public class StopwatcherData
{
public long Time { get; set; }
public string Message { get; set; }
public StopwatcherData(long time, string message)
{
Time = time;
Message = message;
}
}
public class Stopwatcher
{
public delegate void CompletedCallBack(string result);
public static List<StopwatcherData> Data { get; set; }
private static Stopwatch stopwatch { get; set;}
public Stopwatcher()
{
Data = new List<StopwatcherData>();
stopwatch = new Stopwatch();
stopwatch.Start();
}
public static void Click(string message)
{
Data.Add(new StopwatcherData(stopwatch.ElapsedMilliseconds, message));
}
public static void Reset()
{
stopwatch.Reset();
stopwatch.Start();
}
}
现在要使用这个,我必须在我想要的功能之前调用重置,以便重新启动计时器,然后在它之后调用点击。
Stopwatcher.Reset()
MyFunction();
Stopwatcher.Click("MyFunction");
我已经阅读了一些有关委托和操作的内容,但我不确定如何将它们应用于这种情况。理想情况下,我会将函数作为 Stopwatcher 调用的一部分传递。
//End Goal:
Stopwatcher.Track(MyFunction(), "MyFunction Time");
欢迎任何帮助。
是的,您可以创建一个计时器函数来接受任何操作作为委托。试试这个区块:
public static long TimeAction(Action action)
{
var timer = new Stopwatch();
timer.Start();
action();
timer.Stop();
return timer.ElapsedMilliseconds;
}
可以这样使用:
var elapsedMilliseconds = TimeAction(() => MyFunc(param1, param2));
如果你的包装函数 returns 是一个值,这会有点尴尬,但你可以通过从闭包中分配一个变量来处理这个问题,如下所示:
bool isSuccess ;
var elapsedMilliseconds = TimeToAction(() => {
isSuccess = MyFunc(param1, param2);
});
这样分析你的应用程序并不是一个好主意,但如果你坚持,你至少可以做一些改进。
首先,不要重复使用 Stopwatch
,只需在每次需要时创建新的即可。
其次,您需要处理两种情况 - 一种是委托您传递了 return 的值,另一种是它没有。
由于您的 Track
方法是静态的 - 使其线程安全是一种常见的做法。非线程安全的静态方法是非常糟糕的主意。为此,您可以将消息存储在线程安全的集合中,例如 ConcurrentBag
,或者每次将项目添加到列表时都使用 lock
。
最后你可以得到这样的结果:
public class Stopwatcher {
private static readonly ConcurrentBag<StopwatcherData> _data = new ConcurrentBag<StopwatcherData>();
public static void Track(Action action, string message) {
var w = Stopwatch.StartNew();
try {
action();
}
finally {
w.Stop();
_data.Add(new StopwatcherData(w.ElapsedMilliseconds, message));
}
}
public static T Track<T>(Func<T> func, string message) {
var w = Stopwatch.StartNew();
try {
return func();
}
finally {
w.Stop();
_data.Add(new StopwatcherData(w.ElapsedMilliseconds, message));
}
}
}
并像这样使用它:
Stopwatcher.Track(() => SomeAction(param1), "test");
bool result = Stopwatcher.Track(() => SomeFunc(param2), "test");
如果您要将其与异步委托(return Task
或 Task<T>
)一起使用 - 您需要为这种情况再添加两个重载。
我前段时间也遇到过这个问题,一直担心当我将 Stopwatcher.Track(() => SomeFunc(), "test")
(参见 Evk 的回答)改回 SomeFunc()
时会留下错误。所以我想办法在不改变它的情况下包装它!
我想到了一个用法,这肯定不是预期的目的。
public class OneTimeStopwatch : IDisposable
{
private string _logPath = "C:\Temp\OneTimeStopwatch.log";
private readonly string _itemname;
private System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
public OneTimeStopwatch(string itemname)
{
_itemname = itemname;
sw.Start();
}
public void Dispose()
{
sw.Stop();
System.IO.File.AppendAllText(_logPath, string.Format($"{_itemname}: {sw.ElapsedMilliseconds}ms{Environment.NewLine}"));
}
}
这个方法很简单
using (new OneTimeStopwatch("test"))
{
//some sensible code not to touch
System.Threading.Thread.Sleep(1000);
}
//logfile with line "test: 1000ms"
我只需要删除 2 行(和自动格式)即可使其恢复正常。 另外,我可以在这里轻松地换行多行,如果不在其他方法中定义新函数,这是不可能的。
同样,对于几毫秒的术语,不建议这样做。