Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions ConcurrentProgramming/Fundamentals/RunMethodAsynchronously.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//____________________________________________________________________________________________________________________________________
//
// Copyright (C) 2025, Mariusz Postol LODZ POLAND.
//
// To be in touch join the community by pressing the `Watch` button and get started commenting using the discussion panel at
//
// https://github.com/mpostol/TP/discussions/182
//
//_____________________________________________________________________________________________________________________________________

using System.ComponentModel;

namespace TP.ConcurrentProgramming.Fundamentals
{
/// <summary>
/// Class that allows running method asynchronously
/// </summary>
/// <remarks>The RunMethodAsynchronously class is designed to run methods asynchronously using delegates.</remarks>
public class RunMethodAsynchronously : IDisposable
{
/// <summary>
///
/// </summary>
/// <param name="operationToRun">Delegate for the method to be executed asynchronously</param>
public RunMethodAsynchronously(DoWorkEventHandler operationToRun, RunWorkerCompletedEventHandler completedHandler)
{
worker = new();
worker.DoWork += operationToRun;
worker.RunWorkerCompleted += completedHandler;
worker.WorkerSupportsCancellation = false;
worker.WorkerReportsProgress = false;
}

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Runs the asynchronously method with parameter
/// </summary>
public void RunAsync(object? argument)
{
if (!worker.IsBusy)
worker.RunWorkerAsync(argument);
}

/// <summary>
/// Runs the asynchronously method without any additional parameters
/// </summary>
public void RunAsync()
{
if (!worker.IsBusy)
worker.RunWorkerAsync();
}

#region private

private readonly BackgroundWorker worker;
private bool disposedValue;

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
worker.Dispose();
}
disposedValue = true;
}
}

#endregion private
}
}
135 changes: 135 additions & 0 deletions ConcurrentProgramming/FundamentalsTest/RunMethodAsynchronouslyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System.ComponentModel;
using TP.ConcurrentProgramming.Fundamentals;

namespace TP.ConcurrentProgramming.FundamentalsTest
{
[TestClass]
public class RunMethodAsynchronouslyTests
{
[TestMethod]
public void Constructor_ShouldAttachEventHandlers()
{
// Arrange
DoWorkEventHandler doWorkHandler = new DoWorkEventHandler((sender, e) => { });
RunWorkerCompletedEventHandler completedHandler = new RunWorkerCompletedEventHandler((sender, e) => { });

// Act
using (RunMethodAsynchronously runner = new RunMethodAsynchronously(doWorkHandler, completedHandler))
{
// Assert
Assert.IsNotNull(runner);
}
}

[TestMethod]
public void RunAsync_WithArgument()
{
// Arrange
DoWorkEventArgs? doWorkEventArgs = null;
int doWorkHandlerCounter = 0;
DoWorkEventHandler doWorkHandler = new DoWorkEventHandler((sender, e) =>
{
doWorkEventArgs = e;
Arguments? argument = e.Argument as Arguments;
if (argument != null)
{
Thread.Sleep(argument.Value); //simulate a long running operation
doWorkEventArgs.Result = "Done";
}
else
throw new ArgumentNullException("Argument is null");
doWorkHandlerCounter++;
});
RunWorkerCompletedEventArgs? runWorkerCompletedEventArgs = null;
int completedHandlerCounter = 0;
RunWorkerCompletedEventHandler completedHandler = new RunWorkerCompletedEventHandler((sender, e) =>
{
runWorkerCompletedEventArgs = e;
completedHandlerCounter++;
});
var runner = new RunMethodAsynchronously(doWorkHandler, completedHandler);
var argument = new Arguments(100);

// Act
runner.RunAsync(argument);
Thread.Sleep(argument.Value * 10);
runner.Dispose();

// Assert

// Assert doWork
Assert.AreEqual<int>(1, doWorkHandlerCounter);
Assert.IsNotNull(doWorkEventArgs);
Assert.IsNotNull(doWorkEventArgs.Argument);
Assert.IsNotNull(doWorkEventArgs.Result);

// Assert completed
Assert.AreEqual<int>(1, completedHandlerCounter);
Assert.IsNotNull(runWorkerCompletedEventArgs);
Assert.IsNull(runWorkerCompletedEventArgs.Error);
Assert.IsFalse(runWorkerCompletedEventArgs.Cancelled);
Assert.IsNotNull(runWorkerCompletedEventArgs.Result);
Assert.IsInstanceOfType(runWorkerCompletedEventArgs.Result, typeof(string));
Assert.AreEqual<string>("Done", (string)runWorkerCompletedEventArgs.Result);
}

[TestMethod]
public void RunAsyncWithoutArgument()
{
// Arrange
int doWorkHandlerCounter = 0;
RunMethodAsynchronously? doWorkSender = null;
DoWorkEventArgs? doWorkEventArgs = null;
DoWorkEventHandler doWorkHandler = new DoWorkEventHandler((sender, e) =>
{
doWorkSender = sender as RunMethodAsynchronously;
doWorkEventArgs = e;
doWorkHandlerCounter++;
});
RunMethodAsynchronously? completedHandlerSender = null;
RunWorkerCompletedEventArgs? runWorkerCompletedEventArgs = null;
int completedHandlerCounter = 0;
RunWorkerCompletedEventHandler completedHandler =
new RunWorkerCompletedEventHandler((sender, e) =>
{
completedHandlerSender = sender as RunMethodAsynchronously;
runWorkerCompletedEventArgs = e;
completedHandlerCounter++;
});
RunMethodAsynchronously runner = new RunMethodAsynchronously(doWorkHandler, completedHandler);

// Act
runner.RunAsync();
Thread.Sleep(1000);
runner.Dispose();

// Assert doWork
Assert.AreEqual<int>(1, doWorkHandlerCounter);
Assert.IsNotNull(doWorkEventArgs);
Assert.IsNull(doWorkEventArgs.Argument);
Assert.IsNull(doWorkEventArgs.Result);

// Assert completed
Assert.AreEqual<int>(1, completedHandlerCounter);
Assert.IsNotNull(runWorkerCompletedEventArgs);
Assert.IsNull(runWorkerCompletedEventArgs.Error);
Assert.IsFalse(runWorkerCompletedEventArgs.Cancelled);
Assert.IsNull(runWorkerCompletedEventArgs.Result);
Assert.AreSame<object>(doWorkSender, completedHandlerSender);
}

#region test instrumentation

private class Arguments
{
public Arguments(int delay)
{
Value = delay;
}

public int Value { get; }
}

#endregion test instrumentation
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net8.0-windows7.0</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<Version>6.0.0</Version>
Expand Down