diff --git a/NullElements/NullElements.Tests/BasicListTests.cs b/NullElements/NullElements.Tests/BasicListTests.cs new file mode 100644 index 0000000..3b8ea18 --- /dev/null +++ b/NullElements/NullElements.Tests/BasicListTests.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +namespace NullElements.Tests; + +public class BasicListTests +{ + private BasicList list; + + [SetUp] + public void Setup() + { + list = []; + } + + [Test] + public void EmptyList_ShouldReturn_EmptyEnumerator() + { + Assert.That(list.SequenceEqual([]), Is.True); + } + + [Test] + public void ListWithOneValue_ShouldReturn_EnumeratorWithOneElement() + { + int value = 42; + list.Add(value); + Assert.That(list.SequenceEqual([value]), Is.True); + } + + [Test] + public void ListWithManyValues_ShouldReturn_EnumeratorWithSameValules() + { + var values = Enumerable.Range(0, 1000); + foreach (var value in values) + { + list.Add(value); + } + + Assert.That(list.SequenceEqual(values), Is.True); + } + + [Test] + public void ModifyingList_WhileIterating_ShouldThrow() + { + var values = Enumerable.Range(0, 1000); + foreach (var value in values) + { + list.Add(value); + } + + var enumerator = list.GetEnumerator(); + enumerator.MoveNext(); + list.Add(1010101); + Assert.Throws(() => enumerator.MoveNext()); + } +} diff --git a/NullElements/NullElements.Tests/GlobalSuppressions.cs b/NullElements/NullElements.Tests/GlobalSuppressions.cs new file mode 100644 index 0000000..5292943 --- /dev/null +++ b/NullElements/NullElements.Tests/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is project with tests")] diff --git a/NullElements/NullElements.Tests/NullElements.Tests.csproj b/NullElements/NullElements.Tests/NullElements.Tests.csproj new file mode 100644 index 0000000..9b9a387 --- /dev/null +++ b/NullElements/NullElements.Tests/NullElements.Tests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + diff --git a/NullElements/NullElements.Tests/NullElementsTests.cs b/NullElements/NullElements.Tests/NullElementsTests.cs new file mode 100644 index 0000000..556f052 --- /dev/null +++ b/NullElements/NullElements.Tests/NullElementsTests.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +namespace NullElements.Tests; + +using System.Numerics; + +public class NullElementsTests +{ + [Test] + public void CountNullElements_IsCorrect_ForReferenceTypes() + { + BasicList list = [null, 42, null, "abc", null]; + Assert.That(list.CountNullElements(new ReferenceNullComparer()), Is.EqualTo(3)); + } + + [Test] + public void CountNullElements_IsCorrect_ForNumberTypes() + { + BasicList intList = [0, 1, 0, 2, 0, 100, 0, -100]; + Assert.That(intList.CountNullElements(new NumberNullComparer()), Is.EqualTo(4)); + + BasicList floatList = [0, 0.1f, 0, 0.2f, 0, 100.100f, 0, -42.42f, 0.00000001f, 0]; + Assert.That(floatList.CountNullElements(new NumberNullComparer()), Is.EqualTo(5)); + } + + [Test] + public void CountNullElements_IsCorrect_ForStrings() + { + BasicList list = [null, string.Empty, "abc", "def", null, "apple", string.Empty]; + Assert.That(list.CountNullElements(new StringNullComparer()), Is.EqualTo(4)); + } + + private class ReferenceNullComparer : INullComparer + where T : class + { + public bool IsNull(T item) => item == null; + } + + private class NumberNullComparer : INullComparer + where T : INumber + { + public bool IsNull(T item) => T.IsZero(item); + } + + private class StringNullComparer : INullComparer + { + public bool IsNull(string item) => string.IsNullOrEmpty(item); + } +} diff --git a/NullElements/NullElements.sln b/NullElements/NullElements.sln new file mode 100644 index 0000000..26494ab --- /dev/null +++ b/NullElements/NullElements.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NullElements", "NullElements\NullElements.csproj", "{2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NullElements.Tests", "NullElements.Tests\NullElements.Tests.csproj", "{D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Release|Any CPU.Build.0 = Release|Any CPU + {D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/NullElements/NullElements/BasicList.cs b/NullElements/NullElements/BasicList.cs new file mode 100644 index 0000000..1a61871 --- /dev/null +++ b/NullElements/NullElements/BasicList.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +namespace NullElements; + +using System.Collections; + +/// +/// Basic array-backed list. +/// +/// Type of items. +public class BasicList : IEnumerable +{ + private T[] values = new T[4]; + private int count = 0; + private int version = 0; + + /// + /// Initializes a new instance of the class. + /// + public BasicList() + { + values = new T[4]; + count = 0; + } + + /// + /// Adds item to the list. + /// + /// Item to add. + public void Add(T item) + { + if (count >= values.Length) + { + var newValues = new T[values.Length * 2]; + Array.Copy(values, newValues, values.Length); + values = newValues; + } + + values[count] = item; + count++; + version++; + } + + /// + public IEnumerator GetEnumerator() + { + int lastVersion = version; + + for (int i = 0; i < count; i++) + { + if (version != lastVersion) + { + throw new InvalidOperationException(); + } + + yield return values[i]; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} diff --git a/NullElements/NullElements/INullComparer.cs b/NullElements/NullElements/INullComparer.cs new file mode 100644 index 0000000..7eae09e --- /dev/null +++ b/NullElements/NullElements/INullComparer.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +namespace NullElements; + +/// +/// Interface that can be used to check whether object is null. +/// +/// Type of object to check. +public interface INullComparer +{ + /// + /// Checks whether specified object is null. + /// + /// Object to check. + /// if specified object is null, otherwise. + public bool IsNull(T item); +} diff --git a/NullElements/NullElements/NullElements.cs b/NullElements/NullElements/NullElements.cs new file mode 100644 index 0000000..43f66c9 --- /dev/null +++ b/NullElements/NullElements/NullElements.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) Ilya Krivtsov. All rights reserved. +// + +namespace NullElements; + +/// +/// Extension class that contains CountNullElements function. +/// +public static class NullElements +{ + /// + /// Counts null elements in the list. + /// + /// Type of objects. + /// List to count objects from. + /// Comparer that checks whether object is null. + /// Count of null elements. + public static int CountNullElements(this BasicList list, INullComparer comparer) + => list.Count(comparer.IsNull); +} diff --git a/NullElements/NullElements/NullElements.csproj b/NullElements/NullElements/NullElements.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/NullElements/NullElements/NullElements.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + +