From a8e13e0416026ea072b48ac7dba9e18a7b27e814 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 3 Jan 2023 18:19:46 +0100 Subject: [PATCH] Enable nullable context for entire solution This is a squashed merge of PR #915 that adds to #803. --- Directory.Build.props | 1 + MoreLinq.Test/AcquireTest.cs | 12 +-- MoreLinq.Test/AggregateTest.cs | 11 +-- MoreLinq.Test/AppendTest.cs | 2 +- MoreLinq.Test/AssertCountTest.cs | 2 +- MoreLinq.Test/AssertTest.cs | 2 +- MoreLinq.Test/Async/AsyncEnumerable.cs | 26 ++++--- MoreLinq.Test/BatchTest.cs | 12 ++- MoreLinq.Test/BreakingCollection.cs | 2 - MoreLinq.Test/BreakingSequence.cs | 9 ++- MoreLinq.Test/ChooseTest.cs | 2 +- MoreLinq.Test/CompareCountTest.cs | 8 +- MoreLinq.Test/Comparer.cs | 8 +- MoreLinq.Test/CountByTest.cs | 16 ++-- MoreLinq.Test/CountDownTest.cs | 10 +-- MoreLinq.Test/Enumerable.cs | 36 +++++---- MoreLinq.Test/EqualityComparer.cs | 10 +-- MoreLinq.Test/EquiZipTest.cs | 4 +- MoreLinq.Test/FlattenTest.cs | 2 - MoreLinq.Test/FullGroupJoinTest.cs | 2 - MoreLinq.Test/IndexByTest.cs | 21 +++-- MoreLinq.Test/InterleaveTest.cs | 2 +- MoreLinq.Test/LagTest.cs | 2 - MoreLinq.Test/LeadTest.cs | 2 - MoreLinq.Test/MaxByTest.cs | 2 - MoreLinq.Test/MemoizeTest.cs | 4 +- MoreLinq.Test/MinByTest.cs | 2 - MoreLinq.Test/MoreLinq.Test.csproj | 1 + MoreLinq.Test/NullArgumentTest.cs | 78 ++++++++++++------- MoreLinq.Test/OrderByTest.cs | 15 +++- MoreLinq.Test/PadStartTest.cs | 2 - MoreLinq.Test/PadTest.cs | 2 - MoreLinq.Test/PartialSortByTest.cs | 2 - MoreLinq.Test/PartialSortTest.cs | 2 - MoreLinq.Test/PartitionTest.cs | 2 - MoreLinq.Test/PermutationsTest.cs | 10 ++- MoreLinq.Test/PrependTest.cs | 2 +- MoreLinq.Test/RankTest.cs | 2 - MoreLinq.Test/ReturnTest.cs | 8 +- MoreLinq.Test/ScanByTest.cs | 34 ++++---- MoreLinq.Test/ScanTest.cs | 6 +- MoreLinq.Test/SequenceReader.cs | 70 ++++------------- MoreLinq.Test/SortedMergeTest.cs | 4 +- MoreLinq.Test/SubjectTest.cs | 22 +++--- MoreLinq.Test/TestingSequence.cs | 4 +- MoreLinq.Test/Throws.cs | 5 +- MoreLinq.Test/ToDelimitedStringTest.cs | 4 +- MoreLinq.Test/TraceTest.cs | 1 - MoreLinq.Test/TransposeTest.cs | 2 +- MoreLinq.Test/WatchableEnumerator.cs | 6 +- MoreLinq.Test/ZipLongestTest.cs | 4 +- MoreLinq.Test/ZipShortestTest.cs | 4 +- MoreLinq/MoreLinq.csproj | 1 - MoreLinq/Reactive/Subject.cs | 2 - .../MoreLinq.ExtensionsGenerator.csproj | 1 - 55 files changed, 245 insertions(+), 263 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f958a257d..11286bf3d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,7 @@ 11 + enable true diff --git a/MoreLinq.Test/AcquireTest.cs b/MoreLinq.Test/AcquireTest.cs index c29d09933..14025320b 100644 --- a/MoreLinq.Test/AcquireTest.cs +++ b/MoreLinq.Test/AcquireTest.cs @@ -26,9 +26,9 @@ public class AcquireTest [Test] public void AcquireAll() { - Disposable a = null; - Disposable b = null; - Disposable c = null; + Disposable? a = null; + Disposable? b = null; + Disposable? c = null; var allocators = MoreEnumerable.From(() => a = new Disposable(), () => b = new Disposable(), @@ -48,9 +48,9 @@ public void AcquireAll() [Test] public void AcquireSome() { - Disposable a = null; - Disposable b = null; - Disposable c = null; + Disposable? a = null; + Disposable? b = null; + Disposable? c = null; var allocators = MoreEnumerable.From(() => a = new Disposable(), () => b = new Disposable(), diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 94b6c4ade..690207c5b 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -65,10 +65,11 @@ into m AccumulatorCount = (m.Instantiation.GetParameters().Length - 2 /* source + resultSelector */) / 2 /* seed + accumulator */, ResultSelectorType = rst, Parameters = - rst.GetMethod("Invoke") - .GetParameters() - .Select(p => Expression.Parameter(p.ParameterType)) - .ToArray(), + rst.GetMethod("Invoke") is { } invoke + ? invoke.GetParameters() + .Select(p => Expression.Parameter(p.ParameterType)) + .ToArray() + : throw new Exception("""Method "Invoke" not found."""), } into m let resultSelector = @@ -96,7 +97,7 @@ into t select new TestCaseData(t.Method, t.Args).SetName(t.Name).Returns(t.Expectation); [TestCaseSource(nameof(AccumulatorsTestSource), new object[] { nameof(Accumulators), 10 })] - public object Accumulators(MethodInfo method, object[] args) => + public object? Accumulators(MethodInfo method, object[] args) => method.Invoke(null, args); [Test] diff --git a/MoreLinq.Test/AppendTest.cs b/MoreLinq.Test/AppendTest.cs index df46b13c7..580a74875 100644 --- a/MoreLinq.Test/AppendTest.cs +++ b/MoreLinq.Test/AppendTest.cs @@ -46,7 +46,7 @@ public void AppendWithEmptyHeadSequence() public void AppendWithNullTail() { var head = new[] { "first", "second" }; - string tail = null; + string? tail = null; var whole = head.Append(tail); whole.AssertSequenceEqual("first", "second", null); } diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index a9bf9ca53..b2b165564 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -106,7 +106,7 @@ public void AssertCountIsLazy() [Test] public void AssertCountWithCollectionIsLazy() { - new BreakingCollection(5).AssertCount(0); + new BreakingCollection(new int[5]).AssertCount(0); } [Test] diff --git a/MoreLinq.Test/AssertTest.cs b/MoreLinq.Test/AssertTest.cs index ff77f609f..e5bd40d9e 100644 --- a/MoreLinq.Test/AssertTest.cs +++ b/MoreLinq.Test/AssertTest.cs @@ -49,7 +49,7 @@ public void AssertSequenceWithValidSomeInvalidElements() public void AssertSequenceWithInvalidElementsAndCustomErrorReturningNull() { var source = new[] { 2, 4, 6, 7, 8, 9 }; - Assert.That(() => source.Assert(n => n % 2 == 0, _ => null).Consume(), + Assert.That(() => source.Assert(n => n % 2 == 0, _ => null!).Consume(), Throws.InvalidOperationException); } diff --git a/MoreLinq.Test/Async/AsyncEnumerable.cs b/MoreLinq.Test/Async/AsyncEnumerable.cs index 6fe745988..9e34f492a 100644 --- a/MoreLinq.Test/Async/AsyncEnumerable.cs +++ b/MoreLinq.Test/Async/AsyncEnumerable.cs @@ -141,7 +141,7 @@ public static IAsyncEnumerable Distinct(this IAsyncEnumerable< public static ValueTask ElementAtAsync(this IAsyncEnumerable source, int index) => LinqEnumerable.ElementAtAsync(source, index); - public static ValueTask ElementAtOrDefaultAsync(this IAsyncEnumerable source, int index) => + public static ValueTask ElementAtOrDefaultAsync(this IAsyncEnumerable source, int index) => LinqEnumerable.ElementAtOrDefaultAsync(source, index); public static IAsyncEnumerable Empty() => @@ -159,10 +159,10 @@ public static ValueTask FirstAsync(this IAsyncEnumerable FirstAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.FirstAsync(source, predicate); - public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source) => + public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source) => LinqEnumerable.FirstOrDefaultAsync(source); - public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => + public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.FirstOrDefaultAsync(source, predicate); public static IAsyncEnumerable GroupBy(this IAsyncEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) => @@ -213,10 +213,10 @@ public static ValueTask LastAsync(this IAsyncEnumerable LastAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.LastAsync(source, predicate); - public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source) => + public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source) => LinqEnumerable.LastOrDefaultAsync(source); - public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => + public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.LastOrDefaultAsync(source, predicate); public static ValueTask LongCountAsync(this IAsyncEnumerable source) => @@ -411,10 +411,10 @@ public static ValueTask SingleAsync(this IAsyncEnumerable SingleAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.SingleAsync(source, predicate); - public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source) => + public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source) => LinqEnumerable.SingleOrDefaultAsync(source); - public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => + public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.SingleOrDefaultAsync(source, predicate); public static IAsyncEnumerable Skip(this IAsyncEnumerable source, int count) => @@ -510,16 +510,20 @@ public static IOrderedAsyncEnumerable ThenByDescending(t public static ValueTask ToArrayAsync(this IAsyncEnumerable source) => LinqEnumerable.ToArrayAsync(source); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, IEqualityComparer comparer) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, IEqualityComparer comparer) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector, comparer); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector, elementSelector); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector, elementSelector, comparer); public static ValueTask> ToListAsync(this IAsyncEnumerable source) => diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index ef923b9dd..3b50d7426 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -339,7 +339,7 @@ public void BatchBucketSelectorCurrentList() { var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); using var pool = new TestArrayPool(); - int[] bucketSelectorItems = null; + int[]? bucketSelectorItems = null; var result = input.Batch(4, pool, current => bucketSelectorItems = current.ToArray(), _ => 0); @@ -356,18 +356,16 @@ public void BatchBucketSelectorCurrentList() sealed class TestArrayPool : ArrayPool, IDisposable { - T[] _pooledArray; - T[] _rentedArray; + T[]? _pooledArray; + T[]? _rentedArray; public override T[] Rent(int minimumLength) { if (_pooledArray is null && _rentedArray is null) _pooledArray = new T[minimumLength * 2]; - if (_pooledArray is null) - throw new InvalidOperationException("The pool is exhausted."); - - (_pooledArray, _rentedArray) = (null, _pooledArray); + (_pooledArray, _rentedArray) = + (null, _pooledArray ?? throw new InvalidOperationException("The pool is exhausted.")); return _rentedArray; } diff --git a/MoreLinq.Test/BreakingCollection.cs b/MoreLinq.Test/BreakingCollection.cs index a628424cf..7cb34e6b9 100644 --- a/MoreLinq.Test/BreakingCollection.cs +++ b/MoreLinq.Test/BreakingCollection.cs @@ -26,8 +26,6 @@ class BreakingCollection : BreakingSequence, ICollection public BreakingCollection(params T[] values) : this ((IList) values) {} public BreakingCollection(IList list) => List = list; - public BreakingCollection(int count) : - this(Enumerable.Repeat(default(T), count).ToList()) {} public int Count => List.Count; diff --git a/MoreLinq.Test/BreakingSequence.cs b/MoreLinq.Test/BreakingSequence.cs index 9ed0ccaae..c3c223311 100644 --- a/MoreLinq.Test/BreakingSequence.cs +++ b/MoreLinq.Test/BreakingSequence.cs @@ -27,7 +27,14 @@ namespace MoreLinq.Test /// class BreakingSequence : IEnumerable { - public IEnumerator GetEnumerator() => throw new InvalidOperationException(); + public IEnumerator GetEnumerator() => throw new BreakException(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } + + sealed class BreakException : Exception + { + public BreakException() { } + public BreakException(string message) : base(message) { } + public BreakException(string message, Exception inner) : base(message, inner) { } + } } diff --git a/MoreLinq.Test/ChooseTest.cs b/MoreLinq.Test/ChooseTest.cs index acc4dd112..3f6baf592 100644 --- a/MoreLinq.Test/ChooseTest.cs +++ b/MoreLinq.Test/ChooseTest.cs @@ -67,7 +67,7 @@ static class Option static class Option { - public static readonly (bool IsSome, T Value) None = (false, default); + public static readonly (bool IsSome, T Value) None = default; } [Test] diff --git a/MoreLinq.Test/CompareCountTest.cs b/MoreLinq.Test/CompareCountTest.cs index bc93e08de..906749bf6 100644 --- a/MoreLinq.Test/CompareCountTest.cs +++ b/MoreLinq.Test/CompareCountTest.cs @@ -53,7 +53,7 @@ public void CompareCountWithCollectionAndSequence(int collectionCount, int expectedCompareCount, int expectedMoveNextCallCount) { - var collection = new BreakingCollection(collectionCount); + var collection = new BreakingCollection(new int[collectionCount]); using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); @@ -70,7 +70,7 @@ public void CompareCountWithSequenceAndCollection(int sequenceCount, int expectedCompareCount, int expectedMoveNextCallCount) { - var collection = new BreakingCollection(collectionCount); + var collection = new BreakingCollection(new int[collectionCount]); using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); @@ -107,7 +107,7 @@ public void CompareCountDisposesSequenceEnumerators() [Test] public void CompareCountDisposesFirstEnumerator() { - var collection = new BreakingCollection(0); + var collection = new BreakingCollection(); using var seq = TestingSequence.Of(); @@ -117,7 +117,7 @@ public void CompareCountDisposesFirstEnumerator() [Test] public void CompareCountDisposesSecondEnumerator() { - var collection = new BreakingCollection(0); + var collection = new BreakingCollection(); using var seq = TestingSequence.Of(); diff --git a/MoreLinq.Test/Comparer.cs b/MoreLinq.Test/Comparer.cs index 75983a3d2..7f9f26c29 100644 --- a/MoreLinq.Test/Comparer.cs +++ b/MoreLinq.Test/Comparer.cs @@ -27,19 +27,19 @@ sealed class Comparer /// . /// - public static IComparer Create(Func compare) => + public static IComparer Create(Func compare) => new DelegatingComparer(compare); sealed class DelegatingComparer : IComparer { - readonly Func _comparer; + readonly Func _comparer; - public DelegatingComparer(Func comparer) + public DelegatingComparer(Func comparer) { _comparer = comparer ?? throw new ArgumentNullException(nameof(comparer)); } - public int Compare(T x, T y) => _comparer(x, y); + public int Compare(T? x, T? y) => _comparer(x, y); } } } diff --git a/MoreLinq.Test/CountByTest.cs b/MoreLinq.Test/CountByTest.cs index ea0e7e8a1..f0732a7ba 100644 --- a/MoreLinq.Test/CountByTest.cs +++ b/MoreLinq.Test/CountByTest.cs @@ -98,10 +98,10 @@ public void CountByWithSomeNullKeys() var result = ss.CountBy(s => s); result.AssertSequenceEqual( - KeyValuePair.Create("foo", 2), - KeyValuePair.Create((string) null, 4), - KeyValuePair.Create("bar", 2), - KeyValuePair.Create("baz", 2)); + KeyValuePair.Create((string?)"foo", 2), + KeyValuePair.Create((string?)null, 4), + KeyValuePair.Create((string?)"bar", 2), + KeyValuePair.Create((string?)"baz", 2)); } [Test] @@ -110,10 +110,10 @@ public void CountByWithSomeNullKeysAndEqualityComparer() var result = new[] { "a", "B", null, "c", "A", null, "b", "A" }.CountBy(c => c, StringComparer.OrdinalIgnoreCase); result.AssertSequenceEqual( - KeyValuePair.Create("a", 3), - KeyValuePair.Create("B", 2), - KeyValuePair.Create((string)null, 2), - KeyValuePair.Create("c", 1)); + KeyValuePair.Create((string?)"a", 3), + KeyValuePair.Create((string?)"B", 2), + KeyValuePair.Create((string?)null, 2), + KeyValuePair.Create((string?)"c", 1)); } } } diff --git a/MoreLinq.Test/CountDownTest.cs b/MoreLinq.Test/CountDownTest.cs index 1e42f40ea..a1862116e 100644 --- a/MoreLinq.Test/CountDownTest.cs +++ b/MoreLinq.Test/CountDownTest.cs @@ -133,14 +133,14 @@ static class TestCollection { public static ICollection Create(ICollection collection, - Func, IEnumerator> em = null) + Func, IEnumerator>? em = null) { return new Collection(collection, em); } public static IReadOnlyCollection CreateReadOnly(ICollection collection, - Func, IEnumerator> em = null) + Func, IEnumerator>? em = null) { return new ReadOnlyCollection(collection, em); } @@ -154,7 +154,7 @@ abstract class Sequence : IEnumerable { readonly Func, IEnumerator> _em; - protected Sequence(Func, IEnumerator> em) => + protected Sequence(Func, IEnumerator>? em) => _em = em ?? (e => e); public IEnumerator GetEnumerator() => @@ -175,7 +175,7 @@ sealed class Collection : Sequence, ICollection readonly ICollection _collection; public Collection(ICollection collection, - Func, IEnumerator> em = null) : + Func, IEnumerator>? em = null) : base(em) => _collection = collection ?? throw new ArgumentNullException(nameof(collection)); @@ -202,7 +202,7 @@ sealed class ReadOnlyCollection : Sequence, IReadOnlyCollection readonly ICollection _collection; public ReadOnlyCollection(ICollection collection, - Func, IEnumerator> em = null) : + Func, IEnumerator>? em = null) : base(em) => _collection = collection ?? throw new ArgumentNullException(nameof(collection)); diff --git a/MoreLinq.Test/Enumerable.cs b/MoreLinq.Test/Enumerable.cs index 0d6f188d3..f7795af4e 100644 --- a/MoreLinq.Test/Enumerable.cs +++ b/MoreLinq.Test/Enumerable.cs @@ -126,7 +126,7 @@ public static int Count(this IEnumerable source, Func(this IEnumerable source) => LinqEnumerable.Count(source); - public static IEnumerable DefaultIfEmpty(this IEnumerable source) => + public static IEnumerable DefaultIfEmpty(this IEnumerable source) => LinqEnumerable.DefaultIfEmpty(source); public static IEnumerable DefaultIfEmpty(this IEnumerable source, TSource defaultValue) => @@ -141,7 +141,7 @@ public static IEnumerable Distinct(this IEnumerable s public static TSource ElementAt(this IEnumerable source, int index) => LinqEnumerable.ElementAt(source, index); - public static TSource ElementAtOrDefault(this IEnumerable source, int index) => + public static TSource? ElementAtOrDefault(this IEnumerable source, int index) => LinqEnumerable.ElementAtOrDefault(source, index); public static IEnumerable Empty() => @@ -159,10 +159,10 @@ public static TSource First(this IEnumerable source) => public static TSource First(this IEnumerable source, Func predicate) => LinqEnumerable.First(source, predicate); - public static TSource FirstOrDefault(this IEnumerable source) => + public static TSource? FirstOrDefault(this IEnumerable source) => LinqEnumerable.FirstOrDefault(source); - public static TSource FirstOrDefault(this IEnumerable source, Func predicate) => + public static TSource? FirstOrDefault(this IEnumerable source, Func predicate) => LinqEnumerable.FirstOrDefault(source, predicate); public static IEnumerable GroupBy(this IEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) => @@ -213,10 +213,10 @@ public static TSource Last(this IEnumerable source) => public static TSource Last(this IEnumerable source, Func predicate) => LinqEnumerable.Last(source, predicate); - public static TSource LastOrDefault(this IEnumerable source) => + public static TSource? LastOrDefault(this IEnumerable source) => LinqEnumerable.LastOrDefault(source); - public static TSource LastOrDefault(this IEnumerable source, Func predicate) => + public static TSource? LastOrDefault(this IEnumerable source, Func predicate) => LinqEnumerable.LastOrDefault(source, predicate); public static long LongCount(this IEnumerable source) => @@ -249,13 +249,13 @@ public static decimal Max(this IEnumerable source, Func(this IEnumerable source, Func selector) => LinqEnumerable.Max(source, selector); - public static TResult Max(this IEnumerable source, Func selector) => + public static TResult? Max(this IEnumerable source, Func selector) => LinqEnumerable.Max(source, selector); public static double? Max(this IEnumerable source, Func selector) => LinqEnumerable.Max(source, selector); - public static TSource Max(this IEnumerable source) => + public static TSource? Max(this IEnumerable source) => LinqEnumerable.Max(source); public static float Max(this IEnumerable source, Func selector) => @@ -303,7 +303,7 @@ public static long Min(this IEnumerable source, Func(this IEnumerable source, Func selector) => LinqEnumerable.Min(source, selector); - public static TResult Min(this IEnumerable source, Func selector) => + public static TResult? Min(this IEnumerable source, Func selector) => LinqEnumerable.Min(source, selector); public static long? Min(this IEnumerable source, Func selector) => @@ -321,7 +321,7 @@ public static decimal Min(this IEnumerable source, Func(this IEnumerable source, Func selector) => LinqEnumerable.Min(source, selector); - public static TSource Min(this IEnumerable source) => + public static TSource? Min(this IEnumerable source) => LinqEnumerable.Min(source); public static double Min(this IEnumerable source, Func selector) => @@ -411,10 +411,10 @@ public static TSource Single(this IEnumerable source) => public static TSource Single(this IEnumerable source, Func predicate) => LinqEnumerable.Single(source, predicate); - public static TSource SingleOrDefault(this IEnumerable source) => + public static TSource? SingleOrDefault(this IEnumerable source) => LinqEnumerable.SingleOrDefault(source); - public static TSource SingleOrDefault(this IEnumerable source, Func predicate) => + public static TSource? SingleOrDefault(this IEnumerable source, Func predicate) => LinqEnumerable.SingleOrDefault(source, predicate); public static IEnumerable Skip(this IEnumerable source, int count) => @@ -510,16 +510,20 @@ public static IOrderedEnumerable ThenByDescending(this I public static TSource[] ToArray(this IEnumerable source) => LinqEnumerable.ToArray(source); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, IEqualityComparer comparer) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, IEqualityComparer comparer) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector, comparer); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector, elementSelector); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector, elementSelector, comparer); public static List ToList(this IEnumerable source) => diff --git a/MoreLinq.Test/EqualityComparer.cs b/MoreLinq.Test/EqualityComparer.cs index f377a819c..655999130 100644 --- a/MoreLinq.Test/EqualityComparer.cs +++ b/MoreLinq.Test/EqualityComparer.cs @@ -27,24 +27,24 @@ static class EqualityComparer /// . /// - public static IEqualityComparer Create(Func comparer) => + public static IEqualityComparer Create(Func comparer) => new DelegatingComparer(comparer); sealed class DelegatingComparer : IEqualityComparer { - readonly Func _comparer; + readonly Func _comparer; readonly Func _hasher; - public DelegatingComparer(Func comparer) + public DelegatingComparer(Func comparer) : this(comparer, x => x == null ? 0 : x.GetHashCode()) {} - DelegatingComparer(Func comparer, Func hasher) + DelegatingComparer(Func comparer, Func hasher) { _comparer = comparer ?? throw new ArgumentNullException(nameof(comparer)); _hasher = hasher ?? throw new ArgumentNullException(nameof(hasher)); } - public bool Equals(T x, T y) => _comparer(x, y); + public bool Equals(T? x, T? y) => _comparer(x, y); public int GetHashCode(T obj) => _hasher(obj); } } diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index c0d18b0af..477a86384 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; @@ -98,7 +96,7 @@ public void ZipDisposesInnerSequencesCaseGetEnumeratorThrows() using var s1 = TestingSequence.Of(1, 2); Assert.That(() => s1.EquiZip(new BreakingSequence(), Tuple.Create).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } } } diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index 2cbbdb940..e1a8a5419 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System.Collections.Generic; diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index 9a73c6ba9..f5435cfa5 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/IndexByTest.cs b/MoreLinq.Test/IndexByTest.cs index da62a738a..8b71ea268 100644 --- a/MoreLinq.Test/IndexByTest.cs +++ b/MoreLinq.Test/IndexByTest.cs @@ -81,18 +81,17 @@ public void IndexByWithSomeNullKeys() var source = new[] { "foo", null, "bar", "baz", null, null, "baz", "bar", null, "foo" }; var result = source.IndexBy(c => c); - const string @null = null; // type inference happiness result.AssertSequenceEqual( - KeyValuePair.Create(0, "foo"), - KeyValuePair.Create(0, @null), - KeyValuePair.Create(0, "bar"), - KeyValuePair.Create(0, "baz"), - KeyValuePair.Create(1, @null), - KeyValuePair.Create(2, @null), - KeyValuePair.Create(1, "baz"), - KeyValuePair.Create(1, "bar"), - KeyValuePair.Create(3, @null), - KeyValuePair.Create(1, "foo")); + KeyValuePair.Create(0, (string?)"foo"), + KeyValuePair.Create(0, (string?)null), + KeyValuePair.Create(0, (string?)"bar"), + KeyValuePair.Create(0, (string?)"baz"), + KeyValuePair.Create(1, (string?)null), + KeyValuePair.Create(2, (string?)null), + KeyValuePair.Create(1, (string?)"baz"), + KeyValuePair.Create(1, (string?)"bar"), + KeyValuePair.Create(3, (string?)null), + KeyValuePair.Create(1, (string?)"foo")); } [Test] diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index ad9b02c6a..8eecc16a3 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -46,7 +46,7 @@ public void TestInterleaveDisposesOnErrorAtGetEnumerator() // Expected and thrown by BreakingSequence Assert.That(() => sequenceA.Interleave(sequenceB).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } /// diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index fbd0262ff..3a1c0fdfd 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index ccff7fb03..39daf7228 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 59c1bc712..0abc1beb6 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/MemoizeTest.cs b/MoreLinq.Test/MemoizeTest.cs index 2d94bdf42..d600afc68 100644 --- a/MoreLinq.Test/MemoizeTest.cs +++ b/MoreLinq.Test/MemoizeTest.cs @@ -343,13 +343,13 @@ sealed class DisposeTestingSequenceEnumerator : IEnumerator { readonly IEnumerator _sequence; - public event EventHandler Disposed; + public event EventHandler? Disposed; public DisposeTestingSequenceEnumerator(IEnumerator sequence) => _sequence = sequence; public T Current => _sequence.Current; - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Reset() => _sequence.Reset(); public bool MoveNext() => _sequence.MoveNext(); diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index d8745c1a6..49c8565a7 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index c27c04025..ac5c2885c 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -56,6 +56,7 @@ + diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index 8e8d0f36d..dc65ad5ad 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -20,12 +20,12 @@ namespace MoreLinq.Test using System; using System.Collections; using System.Collections.Generic; - using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using NUnit.Framework; using NUnit.Framework.Interfaces; + using StackTrace = System.Diagnostics.StackTrace; [TestFixture] public class NullArgumentTest @@ -41,7 +41,7 @@ public void CanBeNull(Action testCase) => static IEnumerable GetNotNullTestCases() => GetTestCases(canBeNull: false, testCaseFactory: (method, args, paramName) => () => { - Exception e = null; + Exception? e = null; try { @@ -53,24 +53,29 @@ static IEnumerable GetNotNullTestCases() => } Assert.That(e, Is.Not.Null, $"No exception was thrown when {nameof(ArgumentNullException)} was expected."); - Assert.That(e, Is.InstanceOf()); - var ane = (ArgumentNullException) e; - Assert.That(ane.ParamName, Is.EqualTo(paramName)); - var stackTrace = new StackTrace(ane, false); + Assert.That(e, Is.InstanceOf().With.Property(nameof(ArgumentNullException.ParamName)).EqualTo(paramName)); + Debug.Assert(e is not null); + var stackTrace = new StackTrace(e, false); var stackFrame = stackTrace.GetFrames().First(); - var actualType = stackFrame.GetMethod().DeclaringType; +#if NETCOREAPP3_1 + // Under .NET Core 3.1, "StackTrace.GetFrames()" was defined to return an array + // of nullable frame elements. See: + // https://github.com/dotnet/corefx/blob/v3.1.32/src/Common/src/CoreLib/System/Diagnostics/StackTrace.cs#L162 + Debug.Assert(stackFrame is not null); +#endif + var actualType = stackFrame.GetMethod()?.DeclaringType; Assert.That(actualType, Is.SameAs(typeof(MoreEnumerable))); }); static IEnumerable GetCanBeNullTestCases() => GetTestCases(canBeNull: true, testCaseFactory: (method, args, _) => () => method.Invoke(null, args)); - static IEnumerable GetTestCases(bool canBeNull, Func testCaseFactory) => + static IEnumerable GetTestCases(bool canBeNull, Func testCaseFactory) => from m in typeof (MoreEnumerable).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) from t in CreateTestCases(m, canBeNull, testCaseFactory) select t; - static IEnumerable CreateTestCases(MethodInfo methodDefinition, bool canBeNull, Func testCaseFactory) + static IEnumerable CreateTestCases(MethodInfo methodDefinition, bool canBeNull, Func testCaseFactory) { var method = InstantiateMethod(methodDefinition); var parameters = method.GetParameters().ToList(); @@ -78,7 +83,7 @@ static IEnumerable CreateTestCases(MethodInfo methodDefinition, b return from param in parameters where IsReferenceType(param) && CanBeNull(param) == canBeNull let arguments = parameters.Select(p => p == param ? null : CreateInstance(p.ParameterType)).ToArray() - let testCase = testCaseFactory(method, arguments, param.Name) + let testCase = testCaseFactory(method, arguments, param.Name ?? throw new NullReferenceException()) let testName = GetTestName(methodDefinition, param) select (ITestCaseData) new TestCaseData(testCase).SetName(testName); } @@ -139,15 +144,25 @@ static object CreateInstance(Type type) if (type == typeof (string)) return ""; if (type == typeof(TaskScheduler)) return TaskScheduler.Default; if (type == typeof(IEnumerable)) return new[] { 1, 2, 3 }; // Provide non-empty sequence for MinBy/MaxBy. - if (type.IsArray) return Array.CreateInstance(type.GetElementType(), 0); - if (type.GetTypeInfo().IsValueType || HasDefaultConstructor(type)) return Activator.CreateInstance(type); if (typeof(Delegate).IsAssignableFrom(type)) return CreateDelegateInstance(type); - var typeInfo = type.GetTypeInfo(); + if (type.IsArray) + { + var elementType = type.GetElementType(); + Debug.Assert(elementType is not null); + return Array.CreateInstance(elementType, 0); + } + + if (type.GetTypeInfo().IsValueType || HasDefaultConstructor(type)) + { + var instance = Activator.CreateInstance(type); + Debug.Assert(instance is not null); + return instance; + } - return typeInfo.IsGenericType - ? CreateGenericInterfaceInstance(typeInfo) - : EmptyEnumerable.Instance; + return type.GetTypeInfo() is { IsGenericType: true } typeInfo + ? CreateGenericInterfaceInstance(typeInfo) + : EmptyEnumerable.Instance; } static bool HasDefaultConstructor(Type type) => @@ -156,6 +171,7 @@ static bool HasDefaultConstructor(Type type) => static Delegate CreateDelegateInstance(Type type) { var invoke = type.GetMethod("Invoke"); + Debug.Assert(invoke is not null); var parameters = invoke.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)); var body = Expression.Default(invoke.ReturnType); // requires >= .NET 4.0 var lambda = Expression.Lambda(type, body, parameters); @@ -167,8 +183,10 @@ static object CreateGenericInterfaceInstance(TypeInfo type) Debug.Assert(type.IsGenericType && type.IsInterface); var name = type.Name.Substring(1); // Delete first character, i.e. the 'I' in IEnumerable var definition = typeof (GenericArgs).GetTypeInfo().GetNestedType(name); - var instantiation = definition.MakeGenericType(type.GetGenericArguments()); - return Activator.CreateInstance(instantiation); + Debug.Assert(definition is not null); + var instance = Activator.CreateInstance(definition.MakeGenericType(type.GetGenericArguments())); + Debug.Assert(instance is not null); + return instance; } static class EmptyEnumerable @@ -191,45 +209,45 @@ public void Reset() { } // ReSharper disable UnusedMember.Local, UnusedAutoPropertyAccessor.Local static class GenericArgs { - class Enumerator : IEnumerator + class Enumerator : IEnumerator { public bool MoveNext() => false; - public T Current { get; private set; } - object IEnumerator.Current => Current; + public T? Current { get; private set; } + object? IEnumerator.Current => Current; public void Reset() { } public void Dispose() { } } - public class Enumerable : IEnumerable + public class Enumerable : IEnumerable { - public IEnumerator GetEnumerator() => new Enumerator(); + public IEnumerator GetEnumerator() => new Enumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - public class OrderedEnumerable : Enumerable, System.Linq.IOrderedEnumerable + public class OrderedEnumerable : Enumerable, System.Linq.IOrderedEnumerable { - public System.Linq.IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) + public System.Linq.IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer? comparer, bool descending) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return this; } } - public class AwaitQuery : Enumerable, - Experimental.IAwaitQuery + public class AwaitQuery : Enumerable, + Experimental.IAwaitQuery { public Experimental.AwaitQueryOptions Options => Experimental.AwaitQueryOptions.Default; - public Experimental.IAwaitQuery WithOptions(Experimental.AwaitQueryOptions options) => this; + public Experimental.IAwaitQuery WithOptions(Experimental.AwaitQueryOptions options) => this; } public class Comparer : IComparer { - public int Compare(T x, T y) => -1; + public int Compare(T? x, T? y) => -1; } public class EqualityComparer : IEqualityComparer { - public bool Equals(T x, T y) => false; + public bool Equals(T? x, T? y) => false; public int GetHashCode(T obj) => 0; } } diff --git a/MoreLinq.Test/OrderByTest.cs b/MoreLinq.Test/OrderByTest.cs index 7cc944c25..cb6f8bf1d 100644 --- a/MoreLinq.Test/OrderByTest.cs +++ b/MoreLinq.Test/OrderByTest.cs @@ -17,6 +17,7 @@ namespace MoreLinq.Test { + using System.Collections.Generic; using NUnit.Framework; /// @@ -45,6 +46,16 @@ public void TestOrderBySelectorPreserved() Assert.That(resultDes1, Is.EqualTo(resultDes2)); } + static readonly IComparer NumericStringComparer = + Comparer.Create((string? a, string? b) => + (a, b) switch + { + (null, null) => 0, + (null, _) => -1, + (_, null) => 1, + var (sa, sb) => int.Parse(sa).CompareTo(int.Parse(sb)) + }); + /// /// Verify that OrderBy preserves the comparer /// @@ -55,7 +66,7 @@ public void TestOrderByComparerPreserved() var sequenceAscending = sequence.Select(x => x.ToString()); var sequenceDescending = sequenceAscending.Reverse(); - var comparer = Comparer.Create((a, b) => int.Parse(a).CompareTo(int.Parse(b))); + var comparer = NumericStringComparer; var resultAsc1 = sequenceAscending.OrderBy(x => x, comparer, OrderByDirection.Descending); var resultAsc2 = sequenceAscending.OrderByDescending(x => x, comparer); @@ -119,7 +130,7 @@ public void TestThenByComparerPreserved() new {A = "2", B = "1"}, }; - var comparer = Comparer.Create((a, b) => int.Parse(a).CompareTo(int.Parse(b))); + var comparer = NumericStringComparer; var resultA1 = sequence.OrderBy(x => x.A, comparer, OrderByDirection.Ascending) .ThenBy(y => y.B, comparer, OrderByDirection.Ascending); diff --git a/MoreLinq.Test/PadStartTest.cs b/MoreLinq.Test/PadStartTest.cs index 2282a28ba..a65e3bd2b 100644 --- a/MoreLinq.Test/PadStartTest.cs +++ b/MoreLinq.Test/PadStartTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/PadTest.cs b/MoreLinq.Test/PadTest.cs index f9677a22d..4537bdca0 100644 --- a/MoreLinq.Test/PadTest.cs +++ b/MoreLinq.Test/PadTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/PartialSortByTest.cs b/MoreLinq.Test/PartialSortByTest.cs index 7330a151c..9c83dcd7c 100644 --- a/MoreLinq.Test/PartialSortByTest.cs +++ b/MoreLinq.Test/PartialSortByTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PartialSortTest.cs b/MoreLinq.Test/PartialSortTest.cs index 53ad4d729..04257a737 100644 --- a/MoreLinq.Test/PartialSortTest.cs +++ b/MoreLinq.Test/PartialSortTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PartitionTest.cs b/MoreLinq.Test/PartitionTest.cs index 2883885c1..cdc4ac19c 100644 --- a/MoreLinq.Test/PartitionTest.cs +++ b/MoreLinq.Test/PartitionTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index 67e5c4446..11d7d3092 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -90,7 +90,7 @@ public void TestCardinalityThreePermutation() // should contain six permutations (as defined above) Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); - Assert.That(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y)))), Is.True); + Assert.That(permutations.All(p => expectedPermutations.Contains(p, SequenceEqualityComparer.Instance)), Is.True); } /// @@ -133,7 +133,7 @@ public void TestCardinalityFourPermutation() // should contain six permutations (as defined above) Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); - Assert.That(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y)))), Is.True); + Assert.That(permutations.All(p => expectedPermutations.Contains(p, SequenceEqualityComparer.Instance)), Is.True); } /// @@ -196,5 +196,11 @@ public void TestPermutationsAreIndependent() } } } + + static class SequenceEqualityComparer + { + public static readonly IEqualityComparer> Instance = + EqualityComparer.Create>((x, y) => x is { } sx && y is { } sy && sx.SequenceEqual(sy)); + } } } diff --git a/MoreLinq.Test/PrependTest.cs b/MoreLinq.Test/PrependTest.cs index 381e1deba..a13e3fb77 100644 --- a/MoreLinq.Test/PrependTest.cs +++ b/MoreLinq.Test/PrependTest.cs @@ -46,7 +46,7 @@ public void PrependWithEmptyTailSequence() public void PrependWithNullHead() { string[] tail = { "second", "third" }; - string head = null; + string? head = null; var whole = tail.Prepend(head); whole.AssertSequenceEqual(null, "second", "third"); } diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index 5fd0a78db..1187e408e 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/ReturnTest.cs b/MoreLinq.Test/ReturnTest.cs index 0cf3f3ee9..29d252632 100644 --- a/MoreLinq.Test/ReturnTest.cs +++ b/MoreLinq.Test/ReturnTest.cs @@ -34,8 +34,8 @@ static class SomeSingleton static class NullSingleton { - public static readonly IEnumerable Sequence = MoreEnumerable.Return(null); - public static IList List => (IList)Sequence; + public static readonly IEnumerable Sequence = MoreEnumerable.Return(null); + public static IList List => (IList)Sequence; } [Test] @@ -77,13 +77,13 @@ public void TestContainsDoesNotThrowWhenTheItemProvidedIsNull() [Test] public void TestIndexOfDoesNotThrowWhenTheItemProvidedIsNull() { - Assert.That(() => NullSingleton.List.IndexOf(new object()), Throws.Nothing); + Assert.That(() => SomeSingleton.List.IndexOf(new object()), Throws.Nothing); } [Test] public void TestIndexOfDoesNotThrowWhenTheItemContainedIsNull() { - Assert.That(() => SomeSingleton.List.IndexOf(null), Throws.Nothing); + Assert.That(() => NullSingleton.List.IndexOf(null), Throws.Nothing); } [Test] diff --git a/MoreLinq.Test/ScanByTest.cs b/MoreLinq.Test/ScanByTest.cs index 343b7bd9d..40414fd3c 100644 --- a/MoreLinq.Test/ScanByTest.cs +++ b/MoreLinq.Test/ScanByTest.cs @@ -50,7 +50,7 @@ public void ScanBy() var result = source.ScanBy( item => item.First(), - key => (Element: default(string), Key: key, State: key - 1), + key => (Element: string.Empty, Key: key, State: key - 1), (state, key, item) => (item, char.ToUpperInvariant(key), state.State + 1)); result.AssertSequenceEqual( @@ -103,31 +103,31 @@ public void ScanByWithSomeNullKeys() var result = source.ScanBy(c => c, _ => -1, (i, _, _) => i + 1); result.AssertSequenceEqual( - KeyValuePair.Create("foo" , 0), - KeyValuePair.Create((string)null, 0), - KeyValuePair.Create("bar" , 0), - KeyValuePair.Create("baz" , 0), - KeyValuePair.Create((string)null, 1), - KeyValuePair.Create((string)null, 2), - KeyValuePair.Create("baz" , 1), - KeyValuePair.Create("bar" , 1), - KeyValuePair.Create((string)null, 3), - KeyValuePair.Create("foo" , 1)); + KeyValuePair.Create((string?)"foo", 0), + KeyValuePair.Create((string?)null , 0), + KeyValuePair.Create((string?)"bar", 0), + KeyValuePair.Create((string?)"baz", 0), + KeyValuePair.Create((string?)null , 1), + KeyValuePair.Create((string?)null , 2), + KeyValuePair.Create((string?)"baz", 1), + KeyValuePair.Create((string?)"bar", 1), + KeyValuePair.Create((string?)null , 3), + KeyValuePair.Create((string?)"foo", 1)); } [Test] public void ScanByWithNullSeed() { - var nil = (object)null; + var nil = (object?)null; var source = new[] { "foo", null, "bar", null, "baz" }; var result = source.ScanBy(c => c, _ => nil, (_, _, _) => nil); result.AssertSequenceEqual( - KeyValuePair.Create("foo" , nil), - KeyValuePair.Create((string)null, nil), - KeyValuePair.Create("bar" , nil), - KeyValuePair.Create((string)null, nil), - KeyValuePair.Create("baz" , nil)); + KeyValuePair.Create((string?)"foo", nil), + KeyValuePair.Create((string?)null , nil), + KeyValuePair.Create((string?)"bar", nil), + KeyValuePair.Create((string?)null , nil), + KeyValuePair.Create((string?)"baz", nil)); } [Test] diff --git a/MoreLinq.Test/ScanTest.cs b/MoreLinq.Test/ScanTest.cs index 15ec08a83..65e6eed10 100644 --- a/MoreLinq.Test/ScanTest.cs +++ b/MoreLinq.Test/ScanTest.cs @@ -47,7 +47,7 @@ public void ScanDoesNotIterateExtra() { var sequence = Enumerable.Range(1, 3).Concat(new BreakingSequence()).Scan(SampleData.Plus); var gold = new[] {1, 3, 6}; - Assert.That(sequence.Consume, Throws.InvalidOperationException); + Assert.That(sequence.Consume, Throws.BreakException); sequence.Take(3).AssertSequenceEqual(gold); } @@ -68,7 +68,7 @@ public void SeededScanSum() [Test] public void SeededScanIsLazy() { - new BreakingSequence().Scan(null, BreakingFunc.Of()); + new BreakingSequence().Scan(null, BreakingFunc.Of()); } [Test] @@ -76,7 +76,7 @@ public void SeededScanDoesNotIterateExtra() { var sequence = Enumerable.Range(1, 3).Concat(new BreakingSequence()).Scan(0, SampleData.Plus); var gold = new[] { 0, 1, 3, 6 }; - Assert.That(sequence.Consume, Throws.InvalidOperationException); + Assert.That(sequence.Consume, Throws.BreakException); sequence.Take(4).AssertSequenceEqual(gold); } diff --git a/MoreLinq.Test/SequenceReader.cs b/MoreLinq.Test/SequenceReader.cs index 1ff809035..5a2a0cba6 100644 --- a/MoreLinq.Test/SequenceReader.cs +++ b/MoreLinq.Test/SequenceReader.cs @@ -35,9 +35,9 @@ public static SequenceReader Read(this IEnumerable source) /// "read" operation. /// /// Type of elements to read. - class SequenceReader : IDisposable + sealed class SequenceReader : IDisposable { - IEnumerator _enumerator; + IEnumerator? _enumerator; /// /// Initializes a instance @@ -63,84 +63,46 @@ static IEnumerator GetEnumerator(IEnumerable source) return source.GetEnumerator(); } + IEnumerator Enumerator => + _enumerator ?? throw new ObjectDisposedException(GetType().FullName); + /// - /// Tires to read the next value. + /// Reads a value otherwise throws + /// if no more values are available. /// - /// - /// When this method returns, contains the value read on success. - /// /// - /// Returns true if a value was successfully read; otherwise, false. + /// Returns the read value; /// - public virtual bool TryRead(out T value) + public T Read() { - EnsureNotDisposed(); + var e = Enumerator; - value = default; - - var e = _enumerator; if (!e.MoveNext()) - return false; + throw new InvalidOperationException(); - value = e.Current; - return true; + return e.Current; } - /// - /// Tires to read the next value otherwise return the default. - /// - - public T TryRead() => TryRead(default); - - /// - /// Tires to read the next value otherwise return a given default. - /// - - public T TryRead(T defaultValue) => - TryRead(out var result) ? result : defaultValue; - - /// - /// Reads a value otherwise throws - /// if no more values are available. - /// - /// - /// Returns the read value; - /// - - public T Read() => - TryRead(out var result) ? result : throw new InvalidOperationException(); - /// /// Reads the end. If the end has not been reached then it /// throws . /// - public virtual void ReadEnd() + public void ReadEnd() { - EnsureNotDisposed(); + var enumerator = Enumerator; - if (_enumerator.MoveNext()) + if (enumerator.MoveNext()) throw new InvalidOperationException(); } - /// - /// Ensures that this object has not been disposed, that - /// has not been previously called. - /// - - protected void EnsureNotDisposed() - { - if (_enumerator == null) - throw new ObjectDisposedException(GetType().FullName); - } - /// /// Disposes this object and enumerator with which is was /// initialized. /// - public virtual void Dispose() + public void Dispose() { var e = _enumerator; if (e == null) return; diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index 3ef82578d..4428e4c5b 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -51,7 +51,7 @@ public void TestSortedMergeDisposesOnError() // Expected and thrown by BreakingSequence Assert.That(() => sequenceA.SortedMerge(OrderByDirection.Ascending, new BreakingSequence()) .Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } /// @@ -62,7 +62,7 @@ public void TestSortedMergeComparerNull() { var sequenceA = Enumerable.Range(1, 3); var sequenceB = Enumerable.Range(4, 3); - var result = sequenceA.SortedMerge(OrderByDirection.Ascending, (IComparer)null, sequenceB); + var result = sequenceA.SortedMerge(OrderByDirection.Ascending, (IComparer?)null, sequenceB); Assert.That(result, Is.EqualTo(sequenceA.Concat(sequenceB))); } diff --git a/MoreLinq.Test/SubjectTest.cs b/MoreLinq.Test/SubjectTest.cs index 334c82730..fe44b2485 100644 --- a/MoreLinq.Test/SubjectTest.cs +++ b/MoreLinq.Test/SubjectTest.cs @@ -25,9 +25,9 @@ namespace MoreLinq.Test public class SubjectTest { static IDisposable Subscribe(IObservable subject, - Action onNext = null, - Action onError = null, - Action onCompleted = null) => + Action? onNext = null, + Action? onError = null, + Action? onCompleted = null) => subject.Subscribe(onNext ?? BreakingAction.Of(), onError ?? BreakingAction.Of(), onCompleted ?? BreakingAction.WithoutArguments); @@ -36,7 +36,7 @@ static IDisposable Subscribe(IObservable subject, public void SubscribeWithNullObserverThrows() { var subject = new Subject(); - Assert.That(() => subject.Subscribe(null), + Assert.That(() => subject.Subscribe(null!), Throws.ArgumentNullException("observer")); } @@ -60,8 +60,8 @@ public void OnNextObservations() [Test] public void OnErrorObservations() { - Exception error1 = null; - Exception error2 = null; + Exception? error1 = null; + Exception? error2 = null; var subject = new Subject(); @@ -139,7 +139,7 @@ public void SubscriptionPostCompletion() [Test] public void SubscriptionPostError() { - Exception observedError = null; + Exception? observedError = null; var subject = new Subject(); var error = new TestException(); subject.OnError(error); @@ -215,8 +215,12 @@ public void ErrorsOnce() public void SafeToDisposeDuringOnNext() { var subject = new Subject(); - IDisposable subscription = null; - var action = new Action(() => subscription.Dispose()); + IDisposable? subscription = null; + var action = new Action(() => + { + Debug.Assert(subscription is not null); + subscription.Dispose(); + }); subscription = subject.Subscribe(_ => action()); subject.OnNext(42); action = BreakingAction.WithoutArguments; diff --git a/MoreLinq.Test/TestingSequence.cs b/MoreLinq.Test/TestingSequence.cs index aa8bf84ee..0a90139d2 100644 --- a/MoreLinq.Test/TestingSequence.cs +++ b/MoreLinq.Test/TestingSequence.cs @@ -41,7 +41,7 @@ internal static TestingSequence AsTestingSequence(this IEnumerable sour sealed class TestingSequence : IEnumerable, IDisposable { bool? _disposed; - IEnumerable _sequence; + IEnumerable? _sequence; internal TestingSequence(IEnumerable sequence) => _sequence = sequence; @@ -65,6 +65,8 @@ void AssertDisposed() public IEnumerator GetEnumerator() { Assert.That(_sequence, Is.Not.Null, "LINQ operators should not enumerate a sequence more than once."); + Debug.Assert(_sequence is not null); + var enumerator = _sequence.GetEnumerator().AsWatchable(); _disposed = false; enumerator.Disposed += delegate diff --git a/MoreLinq.Test/Throws.cs b/MoreLinq.Test/Throws.cs index ef4326408..672234e3c 100644 --- a/MoreLinq.Test/Throws.cs +++ b/MoreLinq.Test/Throws.cs @@ -24,7 +24,8 @@ static class Throws { public static ThrowsNothingConstraint Nothing => NUnit.Framework.Throws.Nothing; public static ExactTypeConstraint InvalidOperationException => NUnit.Framework.Throws.InvalidOperationException; - public static ExactTypeConstraint ObjectDisposedException => NUnit.Framework.Throws.TypeOf(); + public static ExactTypeConstraint ObjectDisposedException => TypeOf(); + public static ExactTypeConstraint BreakException => TypeOf(); public static InstanceOfTypeConstraint InstanceOf() where T : Exception => @@ -41,7 +42,7 @@ public static EqualConstraint ArgumentNullException(string expectedParamName) => NUnit.Framework.Throws.ArgumentNullException.With.ParamName().EqualTo(expectedParamName); public static ExactTypeConstraint ArgumentOutOfRangeException() => - NUnit.Framework.Throws.TypeOf(); + TypeOf(); public static EqualConstraint ArgumentOutOfRangeException(string expectedParamName) => ArgumentOutOfRangeException().With.ParamName().EqualTo(expectedParamName); diff --git a/MoreLinq.Test/ToDelimitedStringTest.cs b/MoreLinq.Test/ToDelimitedStringTest.cs index 8d7eb1f0a..61d21e659 100644 --- a/MoreLinq.Test/ToDelimitedStringTest.cs +++ b/MoreLinq.Test/ToDelimitedStringTest.cs @@ -32,7 +32,7 @@ public void ToDelimitedStringWithNonEmptySequenceAndDelimiter() [Test] public void ToDelimitedStringWithNonEmptySequenceContainingNulls() { - var result = new object[] { 1, null, "foo", true }.ToDelimitedString(","); + var result = new object?[] { 1, null, "foo", true }.ToDelimitedString(","); Assert.That(result, Is.EqualTo("1,,foo,True")); } @@ -40,7 +40,7 @@ public void ToDelimitedStringWithNonEmptySequenceContainingNulls() public void ToDelimitedStringWithNonEmptySequenceContainingNullsAtStart() { // See: https://github.com/morelinq/MoreLINQ/issues/43 - var result = new object[] { null, null, "foo" }.ToDelimitedString(","); + var result = new object?[] { null, null, "foo" }.ToDelimitedString(","); Assert.That(result, Is.EqualTo(",,foo")); } } diff --git a/MoreLinq.Test/TraceTest.cs b/MoreLinq.Test/TraceTest.cs index 36a7ca0ed..65dc4f2b3 100644 --- a/MoreLinq.Test/TraceTest.cs +++ b/MoreLinq.Test/TraceTest.cs @@ -84,7 +84,6 @@ static IEnumerable Lines(string str) IEnumerator _(TextReader reader) { - Debug.Assert(reader != null); while (reader.ReadLine() is { } line) yield return line; } diff --git a/MoreLinq.Test/TransposeTest.cs b/MoreLinq.Test/TransposeTest.cs index f400a0c7c..70f3c6772 100644 --- a/MoreLinq.Test/TransposeTest.cs +++ b/MoreLinq.Test/TransposeTest.cs @@ -36,7 +36,7 @@ public void TransposeWithOneNullRow() using var seq1 = TestingSequence.Of(10, 11); using var seq2 = TestingSequence.Of(); using var seq3 = TestingSequence.Of(30, 31, 32); - using var matrix = TestingSequence.Of(seq1, seq2, seq3, null); + using var matrix = TestingSequence.Of>(seq1, seq2, seq3, null!); Assert.That(() => matrix.Transpose().FirstOrDefault(), Throws.TypeOf()); diff --git a/MoreLinq.Test/WatchableEnumerator.cs b/MoreLinq.Test/WatchableEnumerator.cs index ca22e87be..2756df448 100644 --- a/MoreLinq.Test/WatchableEnumerator.cs +++ b/MoreLinq.Test/WatchableEnumerator.cs @@ -31,14 +31,14 @@ sealed class WatchableEnumerator : IEnumerator { readonly IEnumerator _source; - public event EventHandler Disposed; - public event EventHandler MoveNextCalled; + public event EventHandler? Disposed; + public event EventHandler? MoveNextCalled; public WatchableEnumerator(IEnumerator source) => _source = source ?? throw new ArgumentNullException(nameof(source)); public T Current => _source.Current; - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Reset() => _source.Reset(); public bool MoveNext() diff --git a/MoreLinq.Test/ZipLongestTest.cs b/MoreLinq.Test/ZipLongestTest.cs index 05fbd663c..24bd03a59 100644 --- a/MoreLinq.Test/ZipLongestTest.cs +++ b/MoreLinq.Test/ZipLongestTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; @@ -81,7 +79,7 @@ public void ZipLongestDisposesInnerSequencesCaseGetEnumeratorThrows() using var s1 = TestingSequence.Of(1, 2); Assert.That(() => s1.ZipLongest(new BreakingSequence(), Tuple.Create).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } } } diff --git a/MoreLinq.Test/ZipShortestTest.cs b/MoreLinq.Test/ZipShortestTest.cs index 9c6c59f04..3a63b3660 100644 --- a/MoreLinq.Test/ZipShortestTest.cs +++ b/MoreLinq.Test/ZipShortestTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; @@ -111,7 +109,7 @@ public void ZipShortestDisposesInnerSequencesCaseGetEnumeratorThrows() using var s1 = TestingSequence.Of(1, 2); Assert.That(() => s1.ZipShortest(new BreakingSequence(), Tuple.Create).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } } } diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index f428cf3e3..dccb0afc0 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -120,7 +120,6 @@ 3.3.2 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0;netstandard2.1;net6.0 - enable false diff --git a/MoreLinq/Reactive/Subject.cs b/MoreLinq/Reactive/Subject.cs index f1fc91a6a..bef962abb 100644 --- a/MoreLinq/Reactive/Subject.cs +++ b/MoreLinq/Reactive/Subject.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Reactive { using System; diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index ee274ddfb..e1c9c8e49 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -3,7 +3,6 @@ Exe net7.0 false - enable