-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Scott Baker
committed
Oct 2, 2024
1 parent
803bc9e
commit 852af27
Showing
2 changed files
with
99 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,105 @@ | ||
using System; | ||
using System.Diagnostics; | ||
|
||
#if !NET20 | ||
using System.Linq.Expressions; | ||
using System.Runtime.CompilerServices; | ||
#endif | ||
|
||
#if !NET20 && !NET35 | ||
using System.Threading.Tasks; | ||
#endif | ||
|
||
namespace UtiliDude | ||
{ | ||
public class CodeTimer : IDisposable | ||
{ | ||
private readonly Stopwatch _stopwatch; | ||
private readonly string _name; | ||
private static readonly object _lock = new object(); | ||
public class CodeTimer : IDisposable | ||
{ | ||
private readonly Stopwatch _stopwatch; | ||
private readonly string _name; | ||
private static readonly object _lock = new object(); | ||
|
||
private CodeTimer(string name) | ||
{ | ||
_name = string.IsNullOrWhiteSpace(name) | ||
? throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace.", nameof(name)) | ||
: name; | ||
private CodeTimer(string name) | ||
{ | ||
_name = string.IsNullOrWhiteSpace(name) | ||
? throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace.", nameof(name)) | ||
: name; | ||
|
||
_stopwatch = Stopwatch.StartNew(); | ||
_stopwatch = Stopwatch.StartNew(); | ||
|
||
Debug.WriteLine($" →→ Starting {_name} timer."); | ||
Lock(() => Debug.Indent()); | ||
} | ||
#if DEBUG | ||
Debug.WriteLine($"Starting {_name} timer."); | ||
#if !NET20 | ||
ExecuteWithLock(() => Debug.Indent()); | ||
#endif | ||
#endif | ||
} | ||
|
||
public static IDisposable Start(string name) => new CodeTimer(name); | ||
public static IDisposable Start(string name) => new CodeTimer(name); | ||
|
||
public void Stop() | ||
{ | ||
public void Stop() | ||
{ | ||
if (_stopwatch.IsRunning) | ||
{ | ||
_stopwatch.Stop(); | ||
Lock(() => Debug.Unindent()); | ||
|
||
#if DEBUG | ||
#if !NET20 | ||
ExecuteWithLock(() => Debug.Unindent()); | ||
#endif | ||
Debug.WriteLine($" ←← {_name} took {_stopwatch.ElapsedMilliseconds}ms."); | ||
} | ||
|
||
public void Dispose() => Stop(); | ||
|
||
|
||
public static void Time(Delegate action) | ||
{ | ||
string methodName = action.Method.Name; | ||
Action actionToInvoke = action as Action ?? (() => action.DynamicInvoke()); | ||
Time(actionToInvoke, methodName); | ||
} | ||
|
||
public static void Time(Expression<Action> actionExpression) | ||
{ | ||
if (actionExpression.Body is MethodCallExpression methodCall) | ||
{ | ||
string methodName = methodCall.Method.Name; | ||
Action actionToInvoke = actionExpression.Compile(); | ||
Time(actionToInvoke, methodName); | ||
} | ||
else | ||
{ | ||
throw new ArgumentException("The expression must be a method call."); | ||
} | ||
} | ||
|
||
private static void Time(Action action, string methodName) | ||
{ | ||
using (Start(methodName)) { action(); } | ||
} | ||
|
||
private void Lock(Action action) | ||
{ | ||
lock (_lock) { action(); } | ||
} | ||
} | ||
#endif | ||
} | ||
} | ||
|
||
public void Dispose() => Stop(); | ||
|
||
// Time for synchronous Actions | ||
public static void Time(Action action) => TimeInternal(action, action.Method.Name); | ||
|
||
#if !NET20 | ||
// Time for Expression-based Actions (not available in .NET 2.0) | ||
public static void Time(Expression<Action> actionExpression) | ||
{ | ||
if (actionExpression.Body is MethodCallExpression methodCall) | ||
{ | ||
string methodName = methodCall.Method.Name; | ||
Action actionToInvoke = actionExpression.Compile(); | ||
TimeInternal(actionToInvoke, methodName); | ||
} | ||
else | ||
{ | ||
throw new ArgumentException("The expression must be a method call."); | ||
} | ||
} | ||
#endif | ||
|
||
private static void TimeInternal(Action action, string methodName) | ||
{ | ||
using (Start(methodName)) | ||
{ | ||
action(); | ||
} | ||
} | ||
|
||
#if !NET20 && !NET35 | ||
// Time for async Func<Task> (not available in .NET 2.0 and .NET 3.5) | ||
public static async Task TimeAsync(Func<Task> action) | ||
{ | ||
string methodName = action.Method.Name; | ||
await TimeInternal(action, methodName); | ||
} | ||
|
||
private static async Task TimeInternal(Func<Task> action, string methodName) | ||
{ | ||
using (Start(methodName)) | ||
{ | ||
await action(); | ||
} | ||
} | ||
#endif | ||
|
||
private void ExecuteWithLock(Action action) | ||
{ | ||
lock (_lock) { action(); } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters