diff --git a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Employee.cs b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Employee.cs index 84aa0039da..15262d8b11 100644 --- a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Employee.cs +++ b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Employee.cs @@ -1,55 +1,58 @@ -namespace ObjectComparisons +namespace ObjectComparisons; + +public partial class Employee : IComparable { - public partial class Employee : IComparable + public int Id { get; set; } + public string? Name { get; set; } + + public Employee(int id, string? name) { - public int Id { get; set; } - public string? Name { get; set; } + Id = id; + Name = name; + } - public Employee(int id, string? name) - { - Id = id; - Name = name; - } + public int CompareTo(Employee? otherEmployee) + { + return otherEmployee is null ? 1 : Id.CompareTo(otherEmployee.Id); + } - public int CompareTo(Employee? otherEmployee) - { - return otherEmployee is null ? 1 : Id.CompareTo(otherEmployee.Id); - } + public static IComparer SortByIdAscending() + { + return new SortByIdAscendingHelper(); + } - public static IComparer SortByIdAscending() - { - return new SortByIdAscendingHelper(); - } + public static IComparer SortByIdDescending() + { + return new SortByIdDescendingHelper(); + } - public static IComparer SortByIdDescending() + public static int CompareEmployeesByIdAscending(Employee? employee1, Employee? employee2) + { + if (employee1 is null) { - return new SortByIdDescendingHelper(); + return employee2 is null ? 0 : -1; } - public static int CompareEmployeesByIdAscending(Employee employee1, Employee employee2) + if (employee2 is null) { - if ((employee1 is null) || (employee2 is null)) - { - return 0; - } + return 1; + } - int value1 = employee1?.Id ?? 0; - int value2 = employee2?.Id ?? 0; + return employee1.Id.CompareTo(employee2.Id); + } - return value1.CompareTo(value2); + public static int CompareEmployeesByIdDescending(Employee? employee1, Employee? employee2) + { + if (employee1 is null) + { + return employee2 is null ? 0 : -1; } - public static int CompareEmployeesByIdDescending(Employee employee1, Employee employee2) + if (employee2 is null) { - if ((employee1 is null) || (employee2 is null)) - { - return 0; - } - - int value1 = employee1?.Id ?? 0; - int value2 = employee2?.Id ?? 0; - - return value2.CompareTo(value1); + return 1; } + + return employee2.Id.CompareTo(employee1.Id); } } diff --git a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisonBenchmark.cs b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisonBenchmark.cs index f9fcc75d19..9ff64a48cc 100644 --- a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisonBenchmark.cs +++ b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisonBenchmark.cs @@ -2,50 +2,53 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Order; -namespace ObjectComparisons +namespace ObjectComparisons; + +[MemoryDiagnoser] +[Orderer(SummaryOrderPolicy.FastestToSlowest)] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams)] +public class ObjectComparisonBenchmark { - [MemoryDiagnoser] - [Orderer(SummaryOrderPolicy.FastestToSlowest)] - [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams)] - public class ObjectComparisonBenchmark + private Employee[] _source = []; + private Employee[] _employees = []; + + [Params(5, 1_000)] + public int Size { get; set; } + + [GlobalSetup] + public void Setup() + { + var ids = Enumerable.Range(1, Size).ToArray(); + new Random(42).Shuffle(ids); + + _source = ids.Select(id => new Employee(id, $"Employee {id}")).ToArray(); + _employees = new Employee[Size]; + } + + [Benchmark] + public Employee[] CompareUsingIComparable() + { + _source.CopyTo(_employees, 0); + Array.Sort(_employees); + + return _employees; + } + + [Benchmark] + public Employee[] CompareUsingIComparer() + { + _source.CopyTo(_employees, 0); + Array.Sort(_employees, Employee.SortByIdAscending()); + + return _employees; + } + + [Benchmark] + public Employee[] CompareUsingComparisonDelegate() { - [Params(10000)] - public int Count { get; set; } - - public Employee[] Employees { get; } = new Employee[5] - { - new Employee(4, "John"), - new Employee(2, "Tom"), - new Employee(1, "Eric"), - new Employee(5, "Dan"), - new Employee(3, "Alen") - }; - - [Benchmark] - public void CompareUsingIComparable() - { - for (int i = 0; i < Count; i++) - { - Array.Sort(Employees); - } - } - - [Benchmark] - public void CompareUsingIComparer() - { - for (int i = 0; i < Count; i++) - { - Array.Sort(Employees, Employee.SortByIdAscending()); - } - } - - [Benchmark] - public void CompareUsingComparisonDelegate() - { - for (int i = 0; i < Count; i++) - { - Array.Sort(Employees, Employee.CompareEmployeesByIdAscending); - } - } + _source.CopyTo(_employees, 0); + Array.Sort(_employees, Employee.CompareEmployeesByIdAscending); + + return _employees; } } diff --git a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisons.csproj b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisons.csproj index a6d8996d10..62be4f867f 100644 --- a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisons.csproj +++ b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/ObjectComparisons.csproj @@ -2,13 +2,13 @@ Exe - net6.0 + net10.0 enable enable - + diff --git a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Program.cs b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Program.cs index eeec61736a..c0dde4625c 100644 --- a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Program.cs +++ b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/Program.cs @@ -53,7 +53,13 @@ PrintEmployeeList(employeeList); -BenchmarkRunner.Run(); +employeeList.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal)); + +Console.WriteLine($"{Environment.NewLine}Sorting by Name using a Lambda Expression"); + +PrintEmployeeList(employeeList); + +BenchmarkRunner.Run(args: args); static void PrintEmployees(Employee[] employees) { @@ -69,4 +75,4 @@ static void PrintEmployeeList(List employees) { Console.WriteLine(employee.Id + "\t\t" + employee.Name); } -} \ No newline at end of file +} diff --git a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdAscendingHelper.cs b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdAscendingHelper.cs index 3a0cc14719..bf19ea50cc 100644 --- a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdAscendingHelper.cs +++ b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdAscendingHelper.cs @@ -1,12 +1,19 @@ -namespace ObjectComparisons +namespace ObjectComparisons; + +public class SortByIdAscendingHelper : IComparer { - public class SortByIdAscendingHelper : IComparer + public int Compare(Employee? firstEmployee, Employee? secondEmployee) { - public int Compare(Employee? firstEmployee, Employee? secondEmployee) + if (firstEmployee is null) { - return ((firstEmployee is null) || (secondEmployee is null)) ? - 0 : - firstEmployee.Id.CompareTo(secondEmployee.Id); + return secondEmployee is null ? 0 : -1; } + + if (secondEmployee is null) + { + return 1; + } + + return firstEmployee.Id.CompareTo(secondEmployee.Id); } } diff --git a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdDescendingHelper.cs b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdDescendingHelper.cs index 889c3b2b11..5e1d8bb916 100644 --- a/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdDescendingHelper.cs +++ b/csharp-intermediate-topics/ObjectComparison/ObjectComparison/SortByIdDescendingHelper.cs @@ -1,12 +1,19 @@ -namespace ObjectComparisons +namespace ObjectComparisons; + +public class SortByIdDescendingHelper : IComparer { - public class SortByIdDescendingHelper : IComparer + public int Compare(Employee? firstEmployee, Employee? secondEmployee) { - public int Compare(Employee? firstEmployee, Employee? secondEmployee) + if (firstEmployee is null) { - return ((firstEmployee is null) || (secondEmployee is null)) ? - 0 : - secondEmployee.Id.CompareTo(firstEmployee.Id); + return secondEmployee is null ? 0 : -1; } + + if (secondEmployee is null) + { + return 1; + } + + return secondEmployee.Id.CompareTo(firstEmployee.Id); } } diff --git a/csharp-intermediate-topics/ObjectComparison/Tests/Tests/EmployeeTests.cs b/csharp-intermediate-topics/ObjectComparison/Tests/Tests/EmployeeTests.cs index 6ccbd091f1..a6df7d8886 100644 --- a/csharp-intermediate-topics/ObjectComparison/Tests/Tests/EmployeeTests.cs +++ b/csharp-intermediate-topics/ObjectComparison/Tests/Tests/EmployeeTests.cs @@ -1,59 +1,128 @@ using ObjectComparisons; using Xunit; -namespace Tests +namespace Tests; + +public class EmployeeTests { - public class EmployeeTests - { - [Fact] - public void EmployeeArray_WhenDefaultSorted_ThenIsOrderedBasedOnId() - { - var employees = GetEmployees(); - - Array.Sort(employees); - - for (int i = 0; i < 4; i++) - { - Assert.True(employees[i].Id < employees[i + 1].Id); - } - } - - [Fact] - public void EmployeeArray_WhenSortedUsingIComparer_ThenIsOrderedBasedOnId() - { - var employees = GetEmployees(); - - Array.Sort(employees, Employee.SortByIdAscending()); - - for (int i = 0; i < 4; i++) - { - Assert.True(employees[i].Id < employees[i + 1].Id); - } - } - - [Fact] - public void EmployeeArray_WhenSortedUsingComparisonDelegate_ThenIsOrderedBasedOnId() - { - var employees = GetEmployees(); - - Array.Sort(employees, Employee.CompareEmployeesByIdAscending); - - for (int i = 0; i < 4; i++) - { - Assert.True(employees[i].Id < employees[i + 1].Id); - } - } - - private static Employee[] GetEmployees() - { - return new Employee[5] - { - new Employee(4, "John"), - new Employee(2, "Tom"), - new Employee(1, "Eric"), - new Employee(5, "Dan"), - new Employee(3, "Alen") - }; - } - } -} \ No newline at end of file + [Fact] + public void EmployeeArray_WhenDefaultSorted_ThenIsOrderedBasedOnId() + { + var employees = GetEmployees(); + + Array.Sort(employees); + + Assert.Equal([1, 2, 3, 4, 5], employees.Select(e => e.Id)); + } + + [Fact] + public void EmployeeArray_WhenSortedUsingIComparer_ThenIsOrderedBasedOnId() + { + var employees = GetEmployees(); + + Array.Sort(employees, Employee.SortByIdAscending()); + + Assert.Equal([1, 2, 3, 4, 5], employees.Select(e => e.Id)); + } + + [Fact] + public void EmployeeArray_WhenSortedUsingDescendingIComparer_ThenIsOrderedBasedOnIdDescending() + { + var employees = GetEmployees(); + + Array.Sort(employees, Employee.SortByIdDescending()); + + Assert.Equal([5, 4, 3, 2, 1], employees.Select(e => e.Id)); + } + + [Fact] + public void EmployeeArray_WhenSortedUsingComparisonDelegate_ThenIsOrderedBasedOnId() + { + var employees = GetEmployees(); + + Array.Sort(employees, Employee.CompareEmployeesByIdAscending); + + Assert.Equal([1, 2, 3, 4, 5], employees.Select(e => e.Id)); + } + + [Fact] + public void EmployeeList_WhenSortedUsingLambda_ThenIsOrderedBasedOnName() + { + var employees = GetEmployees().ToList(); + + employees.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal)); + + Assert.Equal(["Alen", "Dan", "Eric", "John", "Tom"], employees.Select(e => e.Name)); + } + + [Fact] + public void EmployeeArrayWithNulls_WhenSortedByEveryApproach_ThenNullsComeFirstAndIdsAreOrdered() + { + Employee?[] expected = [null, null, .. GetEmployees().OrderBy(e => e.Id)]; + + Employee?[] byIComparable = GetEmployeesWithNulls(); + Array.Sort(byIComparable); + + Employee?[] byIComparer = GetEmployeesWithNulls(); + Array.Sort(byIComparer, Comparer.Create(new SortByIdAscendingHelper().Compare)); + + Employee?[] byDelegate = GetEmployeesWithNulls(); + Array.Sort(byDelegate, Employee.CompareEmployeesByIdAscending); + + Assert.Equal(expected.Select(e => e?.Id), byIComparable.Select(e => e?.Id)); + Assert.Equal(expected.Select(e => e?.Id), byIComparer.Select(e => e?.Id)); + Assert.Equal(expected.Select(e => e?.Id), byDelegate.Select(e => e?.Id)); + } + + [Fact] + public void RandomEmployees_WhenSortedByEveryApproach_ThenMatchLinqOrderBy() + { + var random = new Random(7); + var employees = Enumerable.Range(0, 500) + .Select(i => new Employee(random.Next(1_000), $"Employee {i}")) + .ToArray(); + var expected = employees.Select(e => e.Id).Order().ToArray(); + + var byIComparable = (Employee[])employees.Clone(); + Array.Sort(byIComparable); + + var byIComparer = (Employee[])employees.Clone(); + Array.Sort(byIComparer, Employee.SortByIdAscending()); + + var byDelegate = (Employee[])employees.Clone(); + Array.Sort(byDelegate, Employee.CompareEmployeesByIdAscending); + + Assert.Equal(expected, byIComparable.Select(e => e.Id)); + Assert.Equal(expected, byIComparer.Select(e => e.Id)); + Assert.Equal(expected, byDelegate.Select(e => e.Id)); + } + + [Fact] + public void SortedSet_WhenGivenComparerCreatedFromLambda_ThenIsOrderedBasedOnName() + { + var employeesByName = new SortedSet( + GetEmployees(), + Comparer.Create((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal))); + + Assert.Equal(["Alen", "Dan", "Eric", "John", "Tom"], employeesByName.Select(e => e.Name)); + } + + private static Employee[] GetEmployees() + { + return + [ + new Employee(4, "John"), + new Employee(2, "Tom"), + new Employee(1, "Eric"), + new Employee(5, "Dan"), + new Employee(3, "Alen") + ]; + } + + private static Employee?[] GetEmployeesWithNulls() + { + var employees = GetEmployees(); + + return [employees[0], null, employees[1], employees[2], null, employees[3], employees[4]]; + } +} diff --git a/csharp-intermediate-topics/ObjectComparison/Tests/Tests/Tests.csproj b/csharp-intermediate-topics/ObjectComparison/Tests/Tests/Tests.csproj index 1f137c7bcd..146a5a6269 100644 --- a/csharp-intermediate-topics/ObjectComparison/Tests/Tests/Tests.csproj +++ b/csharp-intermediate-topics/ObjectComparison/Tests/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 enable enable @@ -9,13 +9,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all