Skip to content
Open
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
31 changes: 31 additions & 0 deletions Lazy/Lazy/Lazy.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32505.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LazyTest", "LazyTest\LazyTest.csproj", "{47362B64-59D5-4275-B189-4AE547D1B8A9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lazy", "Lazy\Lazy.csproj", "{0067E211-7EEC-40BB-AF0E-B3E2BFE38A83}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{47362B64-59D5-4275-B189-4AE547D1B8A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47362B64-59D5-4275-B189-4AE547D1B8A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47362B64-59D5-4275-B189-4AE547D1B8A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47362B64-59D5-4275-B189-4AE547D1B8A9}.Release|Any CPU.Build.0 = Release|Any CPU
{0067E211-7EEC-40BB-AF0E-B3E2BFE38A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0067E211-7EEC-40BB-AF0E-B3E2BFE38A83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0067E211-7EEC-40BB-AF0E-B3E2BFE38A83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0067E211-7EEC-40BB-AF0E-B3E2BFE38A83}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1DDBE986-F70C-460F-BAC3-5E300605253C}
EndGlobalSection
EndGlobal
14 changes: 14 additions & 0 deletions Lazy/Lazy/Lazy/ILazy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Lazy;

/// <summary>
/// Interface representing lazy computation
/// </summary>
/// <typeparam name="T">Element type</typeparam>
public interface ILazy<T>
{
/// <summary>
/// Function that calls the calculation and returns the result
/// </summary>
/// <returns>Returns the result of the called function</returns>
T? Get();
}
27 changes: 27 additions & 0 deletions Lazy/Lazy/Lazy/Lazy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Lazy;

/// <summary>
/// Abstract class, for subsequent implementation of the ILazy interface
/// </summary>
/// <typeparam name="T">Element type</typeparam>
public abstract class Lazy<T> : ILazy<T>
{
protected Func<T>? func;
protected T? value;

/// <summary>
/// Сonstructor
/// </summary>
/// <param name="func">The object on the basis of which the calculation is performed</param>
public Lazy(Func<T> func)
{
this.func = func;
}

/// <summary>
/// Function that calls a calculation and returns results
/// </summary>
/// <returns>Element type</returns>
public abstract T? Get();
}

10 changes: 10 additions & 0 deletions Lazy/Lazy/Lazy/Lazy.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
48 changes: 48 additions & 0 deletions Lazy/Lazy/Lazy/MultithreadedLazy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace Lazy;

/// <summary>
/// Multithreaded implementation of Lazy
/// </summary>
/// <typeparam name="T">Element type</typeparam>
public class MultithreadedLazy<T> : Lazy<T>
{
/// <summary>
/// Boolean variable for detecting the first call
/// </summary>
private bool isAlreadyCounted;

/// <summary>
/// Lock
/// </summary>
private readonly object lockObject = new();

/// <inheritdoc/>
public MultithreadedLazy(Func<T> func) : base(func) { }

/// <inheritdoc/>
public override T? Get()
{
// if the value has already been calculated, there should be no locks
if (!Volatile.Read(ref isAlreadyCounted))
{
// lock
lock (lockObject)
{
// if the value was not calculated
if (!Volatile.Read(ref isAlreadyCounted))
{
if (func == null)
{
throw new InvalidOperationException();
}

value = func();
func = null;
Volatile.Write(ref isAlreadyCounted, true);
}
}
}

return value;
}
}
30 changes: 30 additions & 0 deletions Lazy/Lazy/Lazy/SingleThreadedLazy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Lazy;

/// <summary>
/// Singlethreaded implementation of Lazy
/// </summary>
/// <typeparam name="T">Element type</typeparam>
public class SingleThreadedLazy<T> : Lazy<T>
{
/// <inheritdoc/>
public SingleThreadedLazy(Func<T> func) : base(func) { }

private bool isAlreadyCounted;

/// <inheritdoc/>
public override T? Get()
{
if (!isAlreadyCounted)
{
if (func == null)
{
throw new InvalidOperationException();
}

value = func();
isAlreadyCounted = true;
}

return value;
}
}
52 changes: 52 additions & 0 deletions Lazy/Lazy/LazyTest/LazyTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace LazyTest;

public class LazyTests
{
private static IEnumerable<TestCaseData> CaseData()
{
var values = new List<int> { 0, -1, 1 };
int number = 0;

static int Increment(int number) => number + 1;

yield return new TestCaseData(new Lazy.SingleThreadedLazy<int>(() => Increment(number)), 1);

foreach (var value in values)
{
yield return new TestCaseData(new Lazy.SingleThreadedLazy<int>(() => value), value);
yield return new TestCaseData(new Lazy.MultithreadedLazy<int>(() => value), value);
}
}

[TestCaseSource(nameof(CaseData))]
public void ShouldValueNotChangeForLazyInSinglethreadedMode(Lazy.ILazy<int> lazy, int expectedValue)
{
for (int i = 0; i < 10; i++)
{
Assert.That(lazy.Get(), Is.EqualTo(expectedValue));
}
}

[Test]
public void ShouldValueNotChangeForLazyInMultithreadedMode()
{
static int Increment(int number) => Interlocked.Increment(ref number);
Lazy.ILazy<int> lazy = new Lazy.MultithreadedLazy<int>(() => Increment(0));
var threads = new Thread[8];

for (int i = 0; i < 8; i++)
{
threads[i] = new Thread(() => Assert.That(lazy.Get(), Is.EqualTo(1)));
}

foreach (var thread in threads)
{
thread.Start();
}

foreach (var thread in threads)
{
thread.Join();
}
}
}
23 changes: 23 additions & 0 deletions Lazy/Lazy/LazyTest/LazyTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lazy\Lazy.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions Lazy/Lazy/LazyTest/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using NUnit.Framework;