From 4549d2775bee050019d21015e95e570bba8189cd Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 1 Nov 2026 17:35:16 +0100 Subject: [PATCH] ParamsKeyword: .NET 10, NUnit 5, params collections examples and tests --- .../ParamsKeyword/ParamsKeyword/Basket.cs | 10 + .../ParamsKeyword/CallParamsMethodUnitTest.cs | 207 +++++++++++------- .../ParamsKeyword/ParamsKeyword.csproj | 11 +- .../ParamsKeyword/ParamsSignatures.cs | 18 ++ .../ParamsKeyword/ShoppingList.cs | 25 ++- 5 files changed, 180 insertions(+), 91 deletions(-) create mode 100644 csharp-basic-topics/ParamsKeyword/ParamsKeyword/Basket.cs create mode 100644 csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsSignatures.cs diff --git a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/Basket.cs b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/Basket.cs new file mode 100644 index 0000000000..6e39c6c3df --- /dev/null +++ b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/Basket.cs @@ -0,0 +1,10 @@ +namespace ParamsKeyword; + +public class Basket +{ + public void Store(params string[] items) + => Console.WriteLine($"Array overload: {items.Length} items"); + + public void Store(params ReadOnlySpan items) + => Console.WriteLine($"Span overload: {items.Length} items"); +} diff --git a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/CallParamsMethodUnitTest.cs b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/CallParamsMethodUnitTest.cs index 487e1506af..ff1813838c 100644 --- a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/CallParamsMethodUnitTest.cs +++ b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/CallParamsMethodUnitTest.cs @@ -1,82 +1,135 @@ using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -namespace ParamsKeyword +namespace ParamsKeyword; + +[TestFixture] +public class CallParamsMethodUnitTest { - [TestFixture] - public class CallParamsMethodUnitTest + private StringWriter _writer = null!; + + [SetUp] + public void CaptureConsole() + { + _writer = new StringWriter(); + Console.SetOut(_writer); + } + + [TearDown] + public void ReleaseConsole() => _writer.Dispose(); + + private string[] Lines() => + _writer.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + + [Test] + public void WhenCommaDelimitedListOfArgs_ThenReturnsValidResult() + { + var shoppingList = new ShoppingList(); + shoppingList.Add("Bananas", "Grapes", "Ham", "Cheese"); + shoppingList.Add("Headphones"); + + string[] expectedResult = + [ + "Added: Bananas", + "Added: Grapes", + "Added: Ham", + "Added: Cheese", + "Added: Headphones", + ]; + + Assert.That(Lines(), Is.EqualTo(expectedResult)); + } + + [Test] + public void WhenCollectionArgument_ThenReturnsValidResult() + { + var shoppingList = new ShoppingList(); + string[] groceries = ["Bananas", "Grapes", "Ham", "Cheese"]; + shoppingList.Add(groceries); + + string[] expectedResult = ["Added: Bananas", "Added: Grapes", "Added: Ham", "Added: Cheese"]; + + Assert.That(Lines(), Is.EqualTo(expectedResult)); + } + + [Test] + public void WhenNoArguments_ThenReturnsValidResult() + { + var shoppingList = new ShoppingList(); + shoppingList.Add(); + + Assert.That(_writer.ToString(), Is.Empty); + } + + [Test] + public void WhenNullArgument_ThenThrowsAnException() + { + var shoppingList = new ShoppingList(); + string[]? groceries = default; + +#pragma warning disable CS8604 // Possible null reference argument. + Assert.Throws(() => shoppingList.Add(groceries)); +#pragma warning restore CS8604 + } + + [Test] + public void WhenParamsIEnumerable_ThenAcceptsListAndLinqResult() + { + var shoppingList = new ShoppingList(); + shoppingList.AddRange("Milk", "Eggs"); + + List pantry = ["Rice", "Beans", "Pasta"]; + shoppingList.AddRange(pantry.Where(item => item.StartsWith('B'))); + + string[] expectedResult = ["Added: Milk", "Added: Eggs", "Added: Beans"]; + + Assert.That(Lines(), Is.EqualTo(expectedResult)); + } + + [Test] + public void WhenParamsReadOnlySpan_ThenCountsArguments() + { + var shoppingList = new ShoppingList(); + + Assert.That(shoppingList.Count("Bananas", "Grapes", "Ham"), Is.EqualTo(3)); + Assert.That(shoppingList.Count(), Is.EqualTo(0)); + } + + [Test] + public void WhenOtherParametersBeforeParams_ThenTheRestGoToParams() + { + Assert.That(ParamsSignatures.StringBeforeStringParams("Required", "And", "Free"), Is.EqualTo("Required + 2")); + Assert.That(ParamsSignatures.ArrayBeforeParams(["Required", "Array"], "And", "Free", "Arguments"), Is.EqualTo("2 + 3")); + Assert.That(ParamsSignatures.ArrayBeforeParams(["Required", "Array"], ["And", "Array", "For Params"]), Is.EqualTo("2 + 3")); + Assert.That(ParamsSignatures.OtherArgumentsBeforeParams(2, true, "Yeah", "Now", "All good"), Is.EqualTo("2, True, 3")); + Assert.That(ParamsSignatures.UseParamsWithNamedArguments(firstArgument: 10, items: ["Now", "all", "good"]), Is.EqualTo("10 + 3")); + } + + [Test] + public void WhenArrayAndSpanOverloads_ThenListPicksSpanAndArrayPicksArray() + { + var basket = new Basket(); + basket.Store("Bananas", "Grapes"); + string[] groceries = ["Bananas", "Grapes"]; + basket.Store(groceries); + + string[] expectedResult = ["Span overload: 2 items", "Array overload: 2 items"]; + + Assert.That(Lines(), Is.EqualTo(expectedResult)); + } + + [Test] + public void WhenArrayPassedToParamsObject_ThenOnlyReferenceTypeArraysExpand() + { + string[] names = ["Bananas", "Grapes"]; + int[] quantities = [3, 5]; + + Assert.That(ParamsSignatures.CountObjects(names), Is.EqualTo(2)); + Assert.That(ParamsSignatures.CountObjects(quantities), Is.EqualTo(1)); + } + + [Test] + public void WhenNamedArgumentWithSingleValue_ThenCompilesAsOneElementArray() { - [Test] - public void WhenCommaDelimitedListOfArgs_ThenReturnsValidResult() - { - //capture the console output - using var writer = new StringWriter(); - Console.SetOut(writer); - - var shoppingList = new ShoppingList(); - shoppingList.Add("Bananas", "Grapes", "Ham", "Cheese"); - shoppingList.Add("Headphones"); - - var expectedResult = new string[] { - "Added: Bananas", - "Added: Grapes", - "Added: Ham", - "Added: Cheese", - "Added: Headphones", - }; - - Assert.AreEqual(expectedResult, writer.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)); - } - - [Test] - public void WhenCollectionArgument_ThenReturnsValidResult() - { - //capture the console output - using var writer = new StringWriter(); - Console.SetOut(writer); - - var shoppingList = new ShoppingList(); - var groceries = new [] { "Bananas", "Grapes", "Ham", "Cheese" }; - shoppingList.Add(groceries); - - var expectedResult = new string[] { - "Added: Bananas", - "Added: Grapes", - "Added: Ham", - "Added: Cheese" - }; - - Assert.AreEqual(expectedResult, writer.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)); - } - - [Test] - public void WhenNoArguments_ThenReturnsValidResult() - { - //capture the console output - using var writer = new StringWriter(); - Console.SetOut(writer); - - var shoppingList = new ShoppingList(); - shoppingList.Add(); - - Assert.AreEqual(string.Empty, writer.ToString()); - } - - [Test] - public void WhenNullArgument_ThenThrowsAnException() - { - //capture the console output - using var writer = new StringWriter(); - Console.SetOut(writer); - - var shoppingList = new ShoppingList(); - string[]? groceries = default; - - #pragma warning disable CS8604 // Possible null reference argument. - Assert.Throws(()=>shoppingList.Add(groceries)); - #pragma warning restore CS8604 // Possible null reference argument. - } + Assert.That(ParamsSignatures.UseParamsWithNamedArguments(firstArgument: 10, items: "Single"), Is.EqualTo("10 + 1")); } -} \ No newline at end of file +} diff --git a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsKeyword.csproj b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsKeyword.csproj index 50852359bb..1eea640068 100644 --- a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsKeyword.csproj +++ b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsKeyword.csproj @@ -1,17 +1,18 @@ - net6.0 + net10.0 + enable enable false - - - - + + + + diff --git a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsSignatures.cs b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsSignatures.cs new file mode 100644 index 0000000000..251cd35506 --- /dev/null +++ b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ParamsSignatures.cs @@ -0,0 +1,18 @@ +namespace ParamsKeyword; + +public static class ParamsSignatures +{ + public static string StringBeforeStringParams(string requiredStringArgument, params string[] items) + => $"{requiredStringArgument} + {items.Length}"; + + public static string ArrayBeforeParams(string[] requiredArray, params string[] items) + => $"{requiredArray.Length} + {items.Length}"; + + public static string OtherArgumentsBeforeParams(int requiredParam, bool optionalBool = true, params string[] items) + => $"{requiredParam}, {optionalBool}, {items.Length}"; + + public static string UseParamsWithNamedArguments(int firstArgument, params string[] items) + => $"{firstArgument} + {items.Length}"; + + public static int CountObjects(params object[] items) => items.Length; +} diff --git a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ShoppingList.cs b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ShoppingList.cs index 30d34c33a5..0588af7d1f 100644 --- a/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ShoppingList.cs +++ b/csharp-basic-topics/ParamsKeyword/ParamsKeyword/ShoppingList.cs @@ -1,15 +1,22 @@ -using System; +namespace ParamsKeyword; -namespace ParamsKeyword +public class ShoppingList { - public class ShoppingList + public void Add(params string[] items) { - public void Add(params string[] items) + foreach (var item in items) { - foreach (var item in items) - { - Console.WriteLine($"Added: {item}"); - } + Console.WriteLine($"Added: {item}"); } } -} \ No newline at end of file + + public void AddRange(params IEnumerable items) + { + foreach (var item in items) + { + Console.WriteLine($"Added: {item}"); + } + } + + public int Count(params ReadOnlySpan items) => items.Length; +}