diff --git a/NullElements/.editorconfig b/NullElements/.editorconfig
new file mode 100644
index 0000000..bc75ec6
--- /dev/null
+++ b/NullElements/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# SA1600: Elements should be documented
+dotnet_diagnostic.SA1600.severity = none
diff --git a/NullElements/NullElements.Tests/NullElements.Tests.csproj b/NullElements/NullElements.Tests/NullElements.Tests.csproj
new file mode 100644
index 0000000..ce411f2
--- /dev/null
+++ b/NullElements/NullElements.Tests/NullElements.Tests.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net9.0
+ latest
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NullElements/NullElements.Tests/NullElementsTests.cs b/NullElements/NullElements.Tests/NullElementsTests.cs
new file mode 100644
index 0000000..3594869
--- /dev/null
+++ b/NullElements/NullElements.Tests/NullElementsTests.cs
@@ -0,0 +1,108 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+namespace NullElements.Tests
+{
+ public class NullElementsTests
+ {
+ [Test]
+ public void Add_NumberToEmptyList_ShouldIncreaseCountByOne()
+ {
+ var testList = new MyList();
+ testList.Add(5);
+
+ Assert.That(testList.Count, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void Add_Overflow_ShouldDoubleInternalArraySize()
+ {
+ var testList = new MyList();
+ var initialCapacity = 10;
+ for (int i = 0; i < initialCapacity + 1; ++i)
+ {
+ testList.Add(i);
+ }
+
+ Assert.That(testList.Count, Is.EqualTo(11));
+ }
+
+ [Test]
+ public void GetEnumerator_ValidData_ShouldIterateInCorrectOrder()
+ {
+ var testList = new MyList();
+ testList.Add(1);
+ testList.Add(2);
+
+ var result = new System.Collections.Generic.List();
+ foreach (var item in testList)
+ {
+ result.Add(item);
+ }
+
+ Assert.That(result, Is.EqualTo(new[] { 1, 2 }));
+ }
+
+ [Test]
+ public void GetEnumerator_WhenListIsEmpty_ShouldNotIterate()
+ {
+ var testList = new MyList();
+
+ var iterations = 0;
+ foreach (var item in testList)
+ {
+ iterations++;
+ }
+
+ Assert.That(iterations, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void CountNulls_String_ShouldReturnCorrectCount()
+ {
+ var testList = new MyList();
+ testList.Add("hello");
+ testList.Add(null);
+ testList.Add(string.Empty);
+ testList.Add("world");
+
+ var result = Counter.CountNulls(testList, new StringNullChecker());
+
+ Assert.That(result, Is.EqualTo(2));
+ }
+
+ [Test]
+ public void CountNulls_Int_ShouldReturnCorrectCountZeros()
+ {
+ var testList = new MyList();
+ testList.Add(0);
+ testList.Add(1);
+ testList.Add(0);
+
+ var result = Counter.CountNulls(testList, new IntNullChecker());
+
+ Assert.That(result, Is.EqualTo(2));
+ }
+
+ [Test]
+ public void CountNulls_WhenIntListIsEmpty_ShouldReturnZero()
+ {
+ var testList = new MyList();
+
+ var result = Counter.CountNulls(testList, new IntNullChecker());
+
+ Assert.That(result, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void CountNulls_WhenStringListIsEmpty_ShouldReturnZero()
+ {
+ var testList = new MyList();
+
+ var result = Counter.CountNulls(testList, new StringNullChecker());
+
+ Assert.That(result, Is.EqualTo(0));
+ }
+ }
+}
diff --git a/NullElements/NullElements.Tests/stylecop.json b/NullElements/NullElements.Tests/stylecop.json
new file mode 100644
index 0000000..577bdb3
--- /dev/null
+++ b/NullElements/NullElements.Tests/stylecop.json
@@ -0,0 +1,15 @@
+{
+ // ACTION REQUIRED: This file was automatically added to your project, but it
+ // will not take effect until additional steps are taken to enable it. See the
+ // following page for additional information:
+ //
+ // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md
+
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "documentationRules": {
+ "companyName": "Kalinin Andrew",
+ "copyrightText": "Copyright (c) {companyName}. All rights reserved."
+ }
+ }
+}
diff --git a/NullElements/NullElements.sln b/NullElements/NullElements.sln
new file mode 100644
index 0000000..285f4bb
--- /dev/null
+++ b/NullElements/NullElements.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35806.99 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NullElements", "NullElements\NullElements.csproj", "{2BA0FF1E-3004-4D5F-A36F-9E7122EE2145}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NullElements.Tests", "NullElements.Tests\NullElements.Tests.csproj", "{1AE28732-923A-4DDE-BE64-28751F09CC28}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36D591C7-65C7-A0D1-1CBC-10CDE441BDC8}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2BA0FF1E-3004-4D5F-A36F-9E7122EE2145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2BA0FF1E-3004-4D5F-A36F-9E7122EE2145}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2BA0FF1E-3004-4D5F-A36F-9E7122EE2145}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2BA0FF1E-3004-4D5F-A36F-9E7122EE2145}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1AE28732-923A-4DDE-BE64-28751F09CC28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1AE28732-923A-4DDE-BE64-28751F09CC28}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1AE28732-923A-4DDE-BE64-28751F09CC28}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1AE28732-923A-4DDE-BE64-28751F09CC28}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {979D0980-DB24-4BC7-909D-644F6A87E14F}
+ EndGlobalSection
+EndGlobal
diff --git a/NullElements/NullElements/Counter.cs b/NullElements/NullElements/Counter.cs
new file mode 100644
index 0000000..3a86309
--- /dev/null
+++ b/NullElements/NullElements/Counter.cs
@@ -0,0 +1,45 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+namespace NullElements
+{
+ ///
+ /// Provides utility methods for working with collections.
+ ///
+ public static class Counter
+ {
+ ///
+ /// Counts elements that match null-equivalent conditions.
+ ///
+ /// The type of elements in the list.
+ /// The collection to inspect.
+ /// The null-equivalence checker implementation.
+ /// The number of null-equivalent elements.
+ /// Thrown when list or checker is null.
+ public static int CountNulls(MyList list, ICheckNulls checker)
+ {
+ if (list == null)
+ {
+ throw new ArgumentNullException(nameof(list));
+ }
+
+ if (checker == null)
+ {
+ throw new ArgumentNullException(nameof(checker));
+ }
+
+ int count = 0;
+
+ foreach (var item in list)
+ {
+ if (checker.IsNull(item))
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+ }
+}
diff --git a/NullElements/NullElements/ICheckNulls.cs b/NullElements/NullElements/ICheckNulls.cs
new file mode 100644
index 0000000..9588f50
--- /dev/null
+++ b/NullElements/NullElements/ICheckNulls.cs
@@ -0,0 +1,20 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+namespace NullElements
+{
+ ///
+ /// Interface for checking if elements meet null-equivalent conditions.
+ ///
+ /// The type of elements to check.
+ public interface ICheckNulls
+ {
+ ///
+ /// Determines if an item should be considered null-equivalent.
+ ///
+ /// The item to check.
+ /// True if the item is considered null-equivalent, otherwise False.
+ bool IsNull(T item);
+ }
+}
diff --git a/NullElements/NullElements/IntNullChecker.cs b/NullElements/NullElements/IntNullChecker.cs
new file mode 100644
index 0000000..566d87c
--- /dev/null
+++ b/NullElements/NullElements/IntNullChecker.cs
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+namespace NullElements
+{
+ ///
+ /// Null-equivalence checker for integer values.
+ ///
+ public class IntNullChecker : ICheckNulls
+ {
+ ///
+ /// Determines if an integer is zero.
+ ///
+ /// The integer to check.
+ /// True if the value is zero, otherwise False.
+ public bool IsNull(int item) => item == 0;
+ }
+}
diff --git a/NullElements/NullElements/MyList.cs b/NullElements/NullElements/MyList.cs
new file mode 100644
index 0000000..7e90b29
--- /dev/null
+++ b/NullElements/NullElements/MyList.cs
@@ -0,0 +1,59 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+using System.Collections;
+
+///
+/// Custom generic list implementation with dynamic resizing.
+///
+/// The type of elements in the list.
+public class MyList : IEnumerable
+{
+ private T[] items;
+
+ private int count;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MyList()
+ {
+ this.items = new T[4];
+ this.count = 0;
+ }
+
+ public int Count => this.count;
+
+ ///
+ /// Adds an item to the end of the list.
+ ///
+ /// The item to add.
+ public void Add(T item)
+ {
+ if (this.count == this.items.Length)
+ {
+ Array.Resize(ref this.items, this.items.Length == 0 ? 10 : this.items.Length * 2);
+ }
+
+ this.items[this.count] = item;
+ this.count++;
+ }
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ /// An enumerator for the list.
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < this.count; ++i)
+ {
+ yield return this.items[i];
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+}
diff --git a/NullElements/NullElements/NullElements.csproj b/NullElements/NullElements/NullElements.csproj
new file mode 100644
index 0000000..091f9ba
--- /dev/null
+++ b/NullElements/NullElements/NullElements.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/NullElements/NullElements/Program.cs b/NullElements/NullElements/Program.cs
new file mode 100644
index 0000000..1350381
--- /dev/null
+++ b/NullElements/NullElements/Program.cs
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+using NullElements;
+
+var intList = new MyList();
+intList.Add(0);
+intList.Add(0);
+intList.Add(0);
+intList.Add(0);
+
+Console.WriteLine(Counter.CountNulls(intList, new IntNullChecker()));
diff --git a/NullElements/NullElements/StringNullChecker.cs b/NullElements/NullElements/StringNullChecker.cs
new file mode 100644
index 0000000..7f87015
--- /dev/null
+++ b/NullElements/NullElements/StringNullChecker.cs
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Kalinin Andrew. All rights reserved.
+//
+
+namespace NullElements
+{
+ ///
+ /// Null-equivalence checker for string values
+ ///
+ public class StringNullChecker : ICheckNulls
+ {
+ ///
+ /// Determines if a string is null or empty.
+ ///
+ /// The string to check.
+ /// True for null or empty strings, otherwise False.
+ public bool IsNull(string item) => item == null || item == string.Empty;
+ }
+}
diff --git a/NullElements/NullElements/stylecop.json b/NullElements/NullElements/stylecop.json
new file mode 100644
index 0000000..577bdb3
--- /dev/null
+++ b/NullElements/NullElements/stylecop.json
@@ -0,0 +1,15 @@
+{
+ // ACTION REQUIRED: This file was automatically added to your project, but it
+ // will not take effect until additional steps are taken to enable it. See the
+ // following page for additional information:
+ //
+ // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md
+
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "documentationRules": {
+ "companyName": "Kalinin Andrew",
+ "copyrightText": "Copyright (c) {companyName}. All rights reserved."
+ }
+ }
+}