Brief
I'm sure you all hate "is this code thread-safe" questions, but I couldn't find a better way to word it.
I've posted a question on CodeReview regarding specific review points, but I'm posting this question on StackOverflow because I think it's a better fit. If I am wrong, please let me know and I will move it CodeReview.
If you prefer to see the code on GitHub, check here
Question
My question amounts to "what problems will I run into using the Task API as below?"--I've never tried working with it before and believe this approach to be erroneous. However, I cannot find an argument for the existence of an error because of my lack of experience. In other words, is this code thread-safe (or whatever appropriate nomenclature fits best), and if not, how can I improve it to be?
Layout
ConsoleLoadingText
: class to encapsulate functionality.
- Constants
- Defaults for
ProductName
,LoadingText
, andMillisecondsDelay
- Array of spinner pieces
- Defaults for
- Constructors
- Chained together constructors allow you to provide some or all of the data, relying on defaults to fill in the gaps. They all delegate to one constructor which does the work.
- Methods
Display
returns a Task which, when run, does the display workStop
stops the display
Code
ConsoleLoadingText.cs
namespace Knoble.Utils
{
/// <summary>
/// A class that represents a possibily infinitely looping load screen.
/// It displays a product name, loading text, and spinner that spins for a given delay.
/// </summary>
public class ConsoleLoadingText
{
public const string DefaultProductName = "";
public const string DefaultLoadingText = "Loading...";
public const int DefaultMillisecondsDelay = 250;
static string[] spinner = { "|", "/", "-", "\\" };
readonly string productName, loadingText;
readonly int millisecondsDelay;
int i;
bool @continue = true;
/// <summary>
/// Initializes a new instance of the <see cref="T:Knoble.Utils.Loading"/> class.
/// Defaults to displaying "Loading... x" where the spinner (x) spins every quarter second.
/// </summary>
public ConsoleLoadingText () : this (DefaultProductName)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Knoble.Utils.Loading"/> class.
/// Defaults to displaying "{productName} Loading... x" where the spinner (x) spins every quarter second.
/// </summary>
/// <param name="productName">Product name.</param>
public ConsoleLoadingText (string productName) : this (productName, DefaultLoadingText)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Knoble.Utils.Loading"/> class.
/// Defaults to displaying "{productName} {loadingText} x" where the spinner (x) spins every quarter second.
/// </summary>
/// <param name="productName">Product name.</param>
/// <param name="loadingText">Loading text.</param>
public ConsoleLoadingText (string productName, string loadingText) : this (productName, loadingText, DefaultMillisecondsDelay)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Knoble.Utils.Loading"/> class.
/// Displays "{productName} {loadingText} x" where the spinner (x) spins every {millisecondsDelay} milliseconds.
/// </summary>
/// <param name="productName">Product name.</param>
/// <param name="loadingText">Loading text.</param>
/// <param name="millisecondsDelay">Milliseconds delay.</param>
public ConsoleLoadingText (string productName, string loadingText, int millisecondsDelay)
{
if (productName == null)
throw new ArgumentException (nameof (productName));
if (loadingText == null)
throw new ArgumentException (nameof (loadingText));
if (millisecondsDelay < 0)
throw new ArgumentException (nameof (millisecondsDelay));
this.productName = productName;
this.loadingText = loadingText;
this.millisecondsDelay = millisecondsDelay;
}
/// <summary>
/// Returns a task that, when running, continously prints the loading text.
/// </summary>
public Task Display ()
{
return Task.Run (() =>
{
@continue = true;
while (@continue)
{
Console.Write ($"\r{productName} {loadingText} {spinner[i]}");
i = (i + 1) % spinner.Length;
Thread.Sleep (millisecondsDelay);
}
});
}
/// <summary>
/// Stop this instance from displaying.
/// </summary>
public void Stop ()
{
@continue = false;
}
}
}