diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/LINQSortingAndFiltering.csproj b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/LINQSortingAndFiltering.csproj index 3e9ed57bbc..2da1acc705 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/LINQSortingAndFiltering.csproj +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/LINQSortingAndFiltering.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net10.0 enable enable diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Program.cs b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Program.cs index 9de39912f9..c989e9aa44 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Program.cs +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Program.cs @@ -45,4 +45,16 @@ shapeList.OfType().PrintList(); Console.WriteLine("\nFilter Is Not Null"); -shapeList.FilterNotNull().PrintList(); \ No newline at end of file +shapeList.FilterNotNull().PrintList(); + +Console.WriteLine("\n--------------------Query Syntax--------------------\n"); + +Console.WriteLine("orderby ShapeType, ShapeId descending"); +var byTypeThenId = + from s in shapeList + orderby s?.ShapeType, s?.ShapeId descending + select s; +byTypeThenId.PrintList(); + +Console.WriteLine("\nOfType"); +shapeList.OfType().PrintList(); \ No newline at end of file diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Shape.cs b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Shape.cs index 96680a005a..6d8a8cefb7 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Shape.cs +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFiltering/Shape.cs @@ -8,9 +8,6 @@ public abstract record Shape : IComparable public int Height { get; init; } public bool Is3D { get; protected init; } - public override string ToString() => - $"ShapeId: {ShapeId} ShapeType: {ShapeType} ShapeWidth: {Width} ShapeHeight: {Height} is3D: {Is3D}"; - public int CompareTo(Shape? other) { if (ReferenceEquals(this, other)) return 0; diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LINQSortingAndFilteringTests.csproj b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LINQSortingAndFilteringTests.csproj index 88eed9fe1a..d6bfe7c263 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LINQSortingAndFilteringTests.csproj +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LINQSortingAndFilteringTests.csproj @@ -1,7 +1,7 @@  - net7.0 + net10.0 enable enable @@ -10,14 +10,13 @@ - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqFilteringMethodsTests.cs b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqFilteringMethodsTests.cs index 0c876e1b19..9d2adbbb7f 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqFilteringMethodsTests.cs +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqFilteringMethodsTests.cs @@ -2,22 +2,22 @@ public class LinqFilteringMethodsTests { - private readonly List _shapes = new() - { - new Cone {ShapeId = 1, Height = 3, Width = 2}, - new Rectangle {ShapeId = 2, Height = 5, Width = 3}, - new Square {ShapeId = 3, Height = 1, Width = 1}, - new Cone {ShapeId = 4, Height = 4, Width = 3}, + private readonly List _shapes = + [ + new Cone { ShapeId = 1, Height = 3, Width = 2 }, + new Rectangle { ShapeId = 2, Height = 5, Width = 3 }, + new Square { ShapeId = 3, Height = 1, Width = 1 }, + new Cone { ShapeId = 4, Height = 4, Width = 3 }, null - }; + ]; [Fact] - public void GivenShapeList_WhenFilterWhereCalled_ThenOnlyShapesWithHeightLessThan4AreReturned() + public void GivenShapeList_WhenFilterWhereCalled_ThenOnlyShapesWithHeightGreaterThan4AreReturned() { var result = _shapes.Where(s => s?.Height > 4).ToArray(); - result.Should().ContainSingle(); - result.First()!.Height.Should().BeGreaterThan(4); + var shape = Assert.Single(result); + Assert.True(shape!.Height > 4); } [Fact] @@ -25,9 +25,9 @@ public void GivenShapeList_WhenFilterWhereVerboseCalled_ThenOnlyShapeThatPassChe { var result = _shapes.Where(LinqFilteringMethods.FilterIs3DAndWidthLessThan3).ToArray(); - result.Should().ContainSingle(); - result.First().Should().BeOfType(); - result.First()!.Width.Should().BeLessThan(3); + var shape = Assert.Single(result); + Assert.IsType(shape); + Assert.True(shape!.Width < 3); } [Fact] @@ -35,9 +35,8 @@ public void GivenShapeList_WhenFilterOfTypeCalled_ThenOnlyExpectedTypesAreReturn { var result = _shapes.OfType().ToArray(); - result.Should().ContainSingle(); - result.First().Should().BeOfType(); - result.First()!.ShapeId.Should().Be(3); + var square = Assert.Single(result); + Assert.Equal(3, square.ShapeId); } [Fact] @@ -45,7 +44,16 @@ public void GivenShapeList_WhenFilterNullExtensionCalled_ThenNullShapesAreRemove { var result = _shapes.FilterNotNull().ToArray(); - result.Should().HaveSameCount(_shapes.Where(x => x is not null)); - result.Should().NotContainNulls(); + Assert.Equal(_shapes.Count(x => x is not null), result.Length); + Assert.All(result, shape => Assert.NotNull(shape)); + } + + [Fact] + public void GivenShapeList_WhenOfTypeBaseType_ThenSameResultAsFilterNotNull() + { + var ofType = _shapes.OfType(); + var filterNotNull = _shapes.FilterNotNull(); + + Assert.Equal(filterNotNull, ofType); } } \ No newline at end of file diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqSortingMethodsTests.cs b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqSortingMethodsTests.cs index 30a3eba13e..63da745e93 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqSortingMethodsTests.cs +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/LinqSortingMethodsTests.cs @@ -2,21 +2,21 @@ namespace LINQSortingAndFilteringTests; public class LinqSortingMethodsTests { - private readonly List _shapes = new() - { - new Rectangle {ShapeId = 2, Height = 4, Width = 2}, - new Square {ShapeId = 1, Height = 2, Width = 2}, - new Cone {ShapeId = 3, Height = 1, Width = 3} - }; + private readonly List _shapes = + [ + new Rectangle { ShapeId = 2, Height = 4, Width = 2 }, + new Square { ShapeId = 1, Height = 2, Width = 2 }, + new Cone { ShapeId = 3, Height = 1, Width = 3 } + ]; [Fact] public void GivenObjectWithoutDefaultOrderer_WhenCallOrder_ThenThrowsInvalidOperationException() { var list = Enumerable.Range(0, 10).Select(_ => new SampleNoOrderer()).ToList(); - var action = () => list.Order(); + var ordered = list.Order(); - action.Enumerating().Should().Throw(); + Assert.Throws(() => ordered.ToList()); } [Fact] @@ -24,20 +24,19 @@ public void GivenObjectWithoutDefaultOrderer_WhenCallOrderByWithPropertyWithOrde { var list = Enumerable.Range(0, 10).Select(_ => new SampleNoOrderer()).ToList(); - var action = () => list.OrderBy(x => x.Id); + var result = list.OrderBy(x => x.Id).ToList(); - action.Enumerating().Should().NotThrow(); + Assert.Equal(list.Count, result.Count); } [Fact] - public void - GivenMemberPropertyWithNoDefaultOrderer_WhenOrderByProperty_ThenThrowsInvalidOperationException() + public void GivenMemberPropertyWithNoDefaultOrderer_WhenOrderByProperty_ThenThrowsInvalidOperationException() { var list = Enumerable.Range(0, 10).Select(_ => new SampleMemberWithNoOrderer()).ToList(); - var action = () => list.OrderBy(x => x.IdHolder); + var ordered = list.OrderBy(x => x.IdHolder); - action.Enumerating().Should().Throw(); + Assert.Throws(() => ordered.ToList()); } [Fact] @@ -45,29 +44,27 @@ public void GivenObjectWithMemberHavingNoDefaultOrderer_WhenCallOrderByOtherProp { var list = Enumerable.Range(0, 10).Select(_ => new SampleMemberWithNoOrderer()).ToList(); - var sortByAAction = () => list.OrderBy(x => x.A); - var sortByBAction = () => list.OrderBy(x => x.B); + var byA = list.OrderBy(x => x.A).ToList(); + var byB = list.OrderBy(x => x.B).ToList(); - sortByAAction.Enumerating().Should().NotThrow(); - sortByBAction.Enumerating().Should().NotThrow(); + Assert.Equal(list.Count, byA.Count); + Assert.Equal(list.Count, byB.Count); } [Fact] public void GivenShapeList_WhenCallingOrder_ThenSortedByDefaultOrdererShapeId() { - var result = _shapes.Order().ToArray(); + var result = _shapes.Order().Select(s => s.ShapeId).ToArray(); - result.Should().HaveSameCount(_shapes); - result.Should().BeInAscendingOrder(s => s.ShapeId); + Assert.Equal([1, 2, 3], result); } [Fact] public void GivenShapeList_WhenCallingOrderDescending_ThenSortedByDefaultOrdererShapeId() { - var result = _shapes.OrderDescending().ToArray(); + var result = _shapes.OrderDescending().Select(s => s.ShapeId).ToArray(); - result.Should().HaveSameCount(_shapes); - result.Should().BeInDescendingOrder(s => s.ShapeId); + Assert.Equal([3, 2, 1], result); } [Fact] @@ -75,19 +72,18 @@ public void GivenShapeList_WhenOrderByShapeType_ThenSortedByShapeType() { var result = _shapes.OrderBy(x => x.ShapeType).ToArray(); - result.Should().HaveSameCount(_shapes); - result.First().Should().BeOfType(); - result.Last().Should().BeOfType(); - result.Should().BeInAscendingOrder(s => s.ShapeType); + Assert.Equal(_shapes.Count, result.Length); + Assert.IsType(result.First()); + Assert.IsType(result.Last()); + Assert.Equal(["Cone", "Rectangle", "Square"], result.Select(s => s.ShapeType)); } [Fact] public void GivenShapeList_WhenOrderByDescending_ThenSortedByShapeIdInDescendingOrder() { - var result = _shapes.OrderByDescending(x => x.ShapeId).ToArray(); + var result = _shapes.OrderByDescending(x => x.ShapeId).Select(s => s.ShapeId).ToArray(); - result.Should().HaveSameCount(_shapes); - result.Should().BeInDescendingOrder(s => s.ShapeId); + Assert.Equal([3, 2, 1], result); } [Fact] @@ -95,44 +91,87 @@ public void GivenShapeList_WhenOrderByIs3DThenByShapeId_ThenSortedByIs3DAndThenS { var result = _shapes.OrderBy(sl => sl.Is3D).ThenBy(sl => sl.ShapeId).ToArray(); - result.Should().HaveSameCount(_shapes); - result[0].Is3D.Should().BeFalse(); - result[1].Is3D.Should().BeFalse(); - result[2].Is3D.Should().BeTrue(); - result[0].ShapeId.Should().Be(1); - result[^1].ShapeId.Should().Be(3); + Assert.False(result[0].Is3D); + Assert.False(result[1].Is3D); + Assert.True(result[2].Is3D); + Assert.Equal(1, result[0].ShapeId); + Assert.Equal(3, result[^1].ShapeId); } [Fact] public void GivenShapeList_WhenOrderByDescendingIs3DAndThenShapeIdDescending_ThenInDescendingOrder() { - // Act var result = _shapes.OrderByDescending(sl => sl.Is3D).ThenByDescending(sl => sl.ShapeId).ToArray(); - // Assert - result.Should().HaveSameCount(_shapes); - result[0].Is3D.Should().BeTrue(); - result[1].Is3D.Should().BeFalse(); - result[2].Is3D.Should().BeFalse(); - result[0].ShapeId.Should().Be(3); - result[^1].ShapeId.Should().Be(1); + Assert.True(result[0].Is3D); + Assert.False(result[1].Is3D); + Assert.False(result[2].Is3D); + Assert.Equal(3, result[0].ShapeId); + Assert.Equal(1, result[^1].ShapeId); } [Fact] public void GivenShapeList_WhenReverse_ThenListIsReversed() { - // Arrange - var listCopy = new List(_shapes); - listCopy.Reverse(); - - // Act var result = _shapes.AsEnumerable().Reverse().ToArray(); - // Assert - result.Should().HaveSameCount(_shapes); - result[0].Should().Be(_shapes[^1]); - result[1].Should().Be(_shapes[^2]); - result[2].Should().Be(_shapes[^3]); + Assert.Equal(_shapes.Count, result.Length); + Assert.Equal(_shapes[^1], result[0]); + Assert.Equal(_shapes[^2], result[1]); + Assert.Equal(_shapes[^3], result[2]); + } + + [Fact] + public void GivenEqualKeys_WhenOrderByAndOrderByDescending_ThenOriginalOrderIsKept() + { + List shapes = + [ + new Square { ShapeId = 7, Height = 1, Width = 1 }, + new Cone { ShapeId = 5, Height = 1, Width = 1 }, + new Square { ShapeId = 2, Height = 1, Width = 1 }, + new Square { ShapeId = 9, Height = 1, Width = 1 } + ]; + + var ascending = shapes.OrderBy(s => s.ShapeType).Select(s => s.ShapeId); + var descending = shapes.OrderByDescending(s => s.ShapeType).Select(s => s.ShapeId); + + Assert.Equal([5, 7, 2, 9], ascending); + Assert.Equal([7, 2, 9, 5], descending); + } + + [Fact] + public void GivenQuerySyntax_WhenOrderByTwoKeys_ThenSameAsOrderByThenByDescending() + { + List shapes = [.. _shapes, null, new Square { ShapeId = 0, Height = 1, Width = 1 }]; + + var querySyntax = + from s in shapes + orderby s?.ShapeType, s?.ShapeId descending + select s; + var methodSyntax = shapes.OrderBy(s => s?.ShapeType).ThenByDescending(s => s?.ShapeId); + + Assert.Equal(methodSyntax, querySyntax); + } + + [Fact] + public void GivenShapeList_WhenSorted_ThenSourceListIsUnchanged() + { + var before = _shapes.ToArray(); + + _ = _shapes.OrderByDescending(s => s.ShapeId).ToList(); + + Assert.Equal(before, _shapes); + } + + [Fact] + public void GivenSortedQuery_WhenSourceChangesBeforeEnumeration_ThenResultIncludesNewItem() + { + var shapes = new List(_shapes); + var ordered = shapes.OrderBy(s => s.ShapeId); + + shapes.Add(new Square { ShapeId = 0, Height = 1, Width = 1 }); + + Assert.Equal([0, 1, 2, 3], ordered.Select(s => s.ShapeId)); } private sealed class SampleNoOrderer diff --git a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/Usings.cs b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/Usings.cs index f4b37ac512..f629a9d495 100644 --- a/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/Usings.cs +++ b/csharp-linq/LINQSortingAndFiltering/LINQSortingAndFilteringTests/Usings.cs @@ -1,3 +1,2 @@ global using Xunit; -global using LINQSortingAndFiltering; -global using FluentAssertions; \ No newline at end of file +global using LINQSortingAndFiltering; \ No newline at end of file