Skip to content

Commit 6f350a3

Browse files
committed
Adds nullable support
1 parent 0b6686c commit 6f350a3

11 files changed

Lines changed: 79 additions & 25 deletions

File tree

samples/ConsoleApp/ConsoleApp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net8.0</TargetFramework>
66
<IsPackable>false</IsPackable>
7+
<Nullable>enable</Nullable>
78
</PropertyGroup>
89

910
<ItemGroup>
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Runtime.CompilerServices;
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Runtime.CompilerServices;
24

35
namespace PlainBytes.System.Extensions.BaseTypes
46
{
@@ -11,29 +13,29 @@ public static class StringExtensions
1113
/// Negation of <see cref="string.IsNullOrEmpty(string)"/>
1214
/// </summary>
1315
[MethodImpl(MethodImplOptions.AggressiveInlining)]
14-
public static bool HasValue(this string value) => !value.IsNullOrEmpty();
16+
public static bool HasValue([NotNullWhen(true)] this string? value) => !value.IsNullOrEmpty();
1517

1618
/// <summary>
1719
/// Negation of <see cref="string.IsNullOrWhiteSpace(string)"/>
1820
/// </summary>
1921
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20-
public static bool HasActualValue(this string value) => !value.IsNullOrWhiteSpace();
22+
public static bool HasActualValue([NotNullWhen(true)] this string? value) => !value.IsNullOrWhiteSpace();
2123

2224
/// <summary>
2325
/// <inheritdoc cref="string.IsNullOrEmpty(string)"/>
2426
/// </summary>
2527
/// <param name="value"><inheritdoc cref="string.IsNullOrEmpty(string)"/></param>
2628
/// <returns><inheritdoc cref="string.IsNullOrEmpty(string)"/></returns>
2729
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28-
public static bool IsNullOrEmpty(this string value) => string.IsNullOrEmpty(value);
30+
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) => string.IsNullOrEmpty(value);
2931

3032
/// <summary>
3133
/// <inheritdoc cref="string.IsNullOrWhiteSpace(string)"/>
3234
/// </summary>
3335
/// <param name="value"><inheritdoc cref="string.IsNullOrWhiteSpace(string)"/></param>
3436
/// <returns><inheritdoc cref="string.IsNullOrWhiteSpace(string)"/></returns>
3537
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36-
public static bool IsNullOrWhiteSpace(this string value) => string.IsNullOrWhiteSpace(value);
38+
public static bool IsNullOrWhiteSpace([NotNullWhen(false)]this string? value) => string.IsNullOrWhiteSpace(value);
3739

3840
/// <summary>
3941
/// <inheritdoc cref="string.Format(string, object[])"/>
@@ -42,6 +44,11 @@ public static class StringExtensions
4244
/// <param name="arguments"><inheritdoc cref="string.Format(string, object[])"/></param>
4345
/// <returns><inheritdoc cref="string.Format(string, object[])"/></returns>
4446
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45-
public static string FormatWith(this string value, params object[] arguments) => string.Format(value, arguments);
47+
public static string FormatWith(this string? value, params object[] arguments)
48+
{
49+
ArgumentNullException.ThrowIfNull(value);
50+
51+
return string.Format(value, arguments);
52+
}
4653
}
4754
}

source/PlainBytes.System.Extensions/BaseTypes/TypeExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ public static class TypeExtensions
2121
/// Returns the name of the type, if generic than with the generic types.
2222
/// </summary>
2323
/// <param name="type">Source type.</param>
24-
public static string GetFormattedName(this Type type)
24+
public static string GetFormattedName(this Type? type)
2525
{
26+
if (type == null)
27+
{
28+
return "null";
29+
}
30+
2631
return FormattedNameCache.GetOrAdd(type, static t =>
2732
{
2833
if (t.IsGenericType)

source/PlainBytes.System.Extensions/Collections/Disposal.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public static class Disposal
1616
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1717
public static void Dispose<T>(this IEnumerable<T> items)
1818
{
19+
ArgumentNullException.ThrowIfNull(items);
20+
1921
foreach (var item in items)
2022
{
2123
if (item is IDisposable disposable)

source/PlainBytes.System.Extensions/Collections/IndexLookup.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.Runtime.CompilerServices;
45

@@ -19,6 +20,8 @@ public static class IndexLookup
1920
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2021
public static bool HasIndex<T>(this IReadOnlyCollection<T> source, int index)
2122
{
23+
ArgumentNullException.ThrowIfNull(source);
24+
2225
return index > -1 && index < source.Count;
2326
}
2427

@@ -30,8 +33,10 @@ public static bool HasIndex<T>(this IReadOnlyCollection<T> source, int index)
3033
/// <param name="index">The index that is being evaluated</param>
3134
/// <returns>The value if the index is valid, otherwise the default of the collection type</returns>
3235
[MethodImpl(MethodImplOptions.AggressiveInlining)]
33-
public static T AtIndexOrDefault<T>(this IReadOnlyList<T> source, int index)
36+
public static T? AtIndexOrDefault<T>(this IReadOnlyList<T> source, int index)
3437
{
38+
ArgumentNullException.ThrowIfNull(source);
39+
3540
if (source.HasIndex(index))
3641
{
3742
return source[index];
@@ -51,6 +56,8 @@ public static T AtIndexOrDefault<T>(this IReadOnlyList<T> source, int index)
5156
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5257
public static bool IsEmpty<T>(this IReadOnlyCollection<T> source)
5358
{
59+
ArgumentNullException.ThrowIfNull(source);
60+
5461
return source.Count == 0;
5562
}
5663

@@ -65,6 +72,8 @@ public static bool IsEmpty<T>(this IReadOnlyCollection<T> source)
6572
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6673
public static T AtIndexOrFallback<T>(this IReadOnlyList<T> source, int index, T fallback)
6774
{
75+
ArgumentNullException.ThrowIfNull(source);
76+
6877
if (source.HasIndex(index))
6978
{
7079
return source[index];
@@ -87,6 +96,8 @@ public static T AtIndexOrFallback<T>(this IReadOnlyList<T> source, int index, T
8796
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8897
public static TValue AtKeyOrFallback<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source, TKey key, TValue fallback)
8998
{
99+
ArgumentNullException.ThrowIfNull(source);
100+
90101
if (source.TryGetValue(key, out var value))
91102
{
92103
return value;
@@ -105,8 +116,10 @@ public static TValue AtKeyOrFallback<TKey, TValue>(this IReadOnlyDictionary<TKey
105116
/// <param name="source">Dictionary that is being evaluated</param>
106117
/// <returns>True if it has any elements, otherwise false.</returns>
107118
[MethodImpl(MethodImplOptions.AggressiveInlining)]
108-
public static bool IsEmpty<TKey, TValue>(this IReadOnlyCollection<KeyValuePair<TKey, TValue>> source)
119+
public static bool IsEmpty<TKey, TValue>(this IReadOnlyCollection<KeyValuePair<TKey, TValue>>? source)
109120
{
121+
ArgumentNullException.ThrowIfNull(source);
122+
110123
return source.Count == 0;
111124
}
112125
}

source/PlainBytes.System.Extensions/Collections/Iterators.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public static class Iterators
2121
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2222
public static void For<T>(this IEnumerable<T> collection, Action<int, T> action)
2323
{
24+
ArgumentNullException.ThrowIfNull(collection);
25+
ArgumentNullException.ThrowIfNull(action);
26+
2427
var index = 0;
2528

2629
foreach (var item in collection)
@@ -41,6 +44,9 @@ public static void For<T>(this IEnumerable<T> collection, Action<int, T> action)
4144
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4245
public static IEnumerable<TR> SelectWithIndex<T, TR>(this IEnumerable<T> collection, Func<int, T, TR> function)
4346
{
47+
ArgumentNullException.ThrowIfNull(collection);
48+
ArgumentNullException.ThrowIfNull(function);
49+
4450
var index = 0;
4551

4652
foreach (var item in collection)
@@ -59,6 +65,9 @@ public static IEnumerable<TR> SelectWithIndex<T, TR>(this IEnumerable<T> collect
5965
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6066
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
6167
{
68+
ArgumentNullException.ThrowIfNull(enumerable);
69+
ArgumentNullException.ThrowIfNull(action);
70+
6271
foreach (var result in enumerable)
6372
{
6473
action(result);
@@ -74,6 +83,8 @@ public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
7483
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7584
public static IEnumerable<TR> SelectTypeOf<TR>(this IEnumerable enumerable)
7685
{
86+
ArgumentNullException.ThrowIfNull(enumerable);
87+
7788
foreach (var element in enumerable)
7889
{
7990
if (element is TR typedElement)
@@ -93,6 +104,8 @@ public static IEnumerable<TR> SelectTypeOf<TR>(this IEnumerable enumerable)
93104
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94105
public static IEnumerable<TR> SelectTypeOf<T, TR>(this IEnumerable<T> enumerable)
95106
{
107+
ArgumentNullException.ThrowIfNull(enumerable);
108+
96109
foreach (var element in enumerable)
97110
{
98111
if (element is TR typedElement)
@@ -112,6 +125,9 @@ public static IEnumerable<TR> SelectTypeOf<T, TR>(this IEnumerable<T> enumerable
112125
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113126
public static IEnumerable<T> Append<T>(this IEnumerable<T> collection, IEnumerable<T> addition)
114127
{
128+
ArgumentNullException.ThrowIfNull(collection);
129+
ArgumentNullException.ThrowIfNull(addition);
130+
115131
foreach (var item in collection)
116132
{
117133
yield return item;
@@ -136,6 +152,9 @@ public static IEnumerable<T> Append<T>(this IEnumerable<T> collection, IEnumerab
136152
[MethodImpl(MethodImplOptions.AggressiveInlining)]
137153
public static async Task ForEachAsync<T>(this IAsyncEnumerable<T> source, Func<T, CancellationToken, ValueTask> action, CancellationToken token = default)
138154
{
155+
ArgumentNullException.ThrowIfNull(source);
156+
ArgumentNullException.ThrowIfNull(action);
157+
139158
await foreach (var item in source.WithCancellation(token))
140159
{
141160
token.ThrowIfCancellationRequested();
@@ -157,6 +176,9 @@ public static async Task ForEachAsync<T>(this IAsyncEnumerable<T> source, Func<T
157176
[MethodImpl(MethodImplOptions.AggressiveInlining)]
158177
public static async IAsyncEnumerable<TR> SelectAsync<T, TR>(this IAsyncEnumerable<T> source, Func<T, TR> selector, [EnumeratorCancellation] CancellationToken token = default)
159178
{
179+
ArgumentNullException.ThrowIfNull(source);
180+
ArgumentNullException.ThrowIfNull(selector);
181+
160182
await foreach (var item in source.WithCancellation(token))
161183
{
162184
token.ThrowIfCancellationRequested();
@@ -178,6 +200,9 @@ public static async IAsyncEnumerable<TR> SelectAsync<T, TR>(this IAsyncEnumerabl
178200
[MethodImpl(MethodImplOptions.AggressiveInlining)]
179201
public static async IAsyncEnumerable<T> WhereAsync<T>(this IAsyncEnumerable<T> source, Func<T, bool> predicate, [EnumeratorCancellation] CancellationToken token = default)
180202
{
203+
ArgumentNullException.ThrowIfNull(source);
204+
ArgumentNullException.ThrowIfNull(predicate);
205+
181206
await foreach (var item in source.WithCancellation(token))
182207
{
183208
token.ThrowIfCancellationRequested();

source/PlainBytes.System.Extensions/PlainBytes.System.Extensions.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
5+
<Nullable>enable</Nullable>
56
<EnablePackageValidation>true</EnablePackageValidation>
67
<GenerateDocumentationFile>True</GenerateDocumentationFile>
78

tests/PlainBytes.System.Extensions.Tests/Collections/AsyncIteratorsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public async Task ForEachAsync_GivenNullSource_ShouldThrow()
2626
IAsyncEnumerable<int> source = null;
2727

2828
// Act / Assert
29-
await Assert.ThrowsAsync<NullReferenceException>(async () => await source.ForEachAsync((i, ct) => ValueTask.CompletedTask));
29+
await Assert.ThrowsAsync<ArgumentNullException>(async () => await source.ForEachAsync((i, ct) => ValueTask.CompletedTask));
3030
}
3131

3232
[Fact]
@@ -74,7 +74,7 @@ public async Task SelectAsync_GivenNullSource_ShouldThrow()
7474
IAsyncEnumerable<int> source = null;
7575

7676
// Act / Assert
77-
await Assert.ThrowsAsync<NullReferenceException>(async () =>
77+
await Assert.ThrowsAsync<ArgumentNullException>(async () =>
7878
{
7979
await foreach (var _ in source.SelectAsync(i => i)) { }
8080
});
@@ -108,7 +108,7 @@ public async Task WhereAsync_GivenNullSource_ShouldThrow()
108108
IAsyncEnumerable<int> source = null;
109109

110110
// Act / Assert
111-
await Assert.ThrowsAsync<NullReferenceException>(async () =>
111+
await Assert.ThrowsAsync<ArgumentNullException>(async () =>
112112
{
113113
await foreach (var _ in source.WhereAsync(i => true)) { }
114114
});

tests/PlainBytes.System.Extensions.Tests/Collections/IndexLookupTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ namespace PlainBytes.System.Extensions.Tests.Collections
99
public class IndexLookupTests
1010
{
1111
[Fact]
12-
public void HasIndex_GivenNullCollection_ThrowsNullReferenceException()
12+
public void HasIndex_GivenNullCollection_ThrowsArgumentNullException()
1313
{
1414
// Arrange
1515
int[] collection = null;
1616

1717
// Act
18-
Assert.Throws<NullReferenceException>(() => collection.HasIndex(1));
18+
Assert.Throws<ArgumentNullException>(() => collection.HasIndex(1));
1919
}
2020

2121
[Fact]
@@ -45,13 +45,13 @@ public void HasIndex_GivenOutOfBoundValue_ReturnFalse()
4545
}
4646

4747
[Fact]
48-
public void AtIndexOrDefault_GivenNullCollection_ThrowsNullReferenceException()
48+
public void AtIndexOrDefault_GivenNullCollection_ThrowsArgumentNullException()
4949
{
5050
// Arrange
5151
int[] collection = null;
5252

5353
// Assert
54-
Assert.Throws<NullReferenceException>(() => collection.AtIndexOrDefault(2));
54+
Assert.Throws<ArgumentNullException>(() => collection.AtIndexOrDefault(2));
5555
}
5656

5757
[Fact]
@@ -81,13 +81,13 @@ public void AtIndexOrDefaultReferenceType_GivenInvalidIndex_ReturnDefault()
8181
}
8282

8383
[Fact]
84-
public void AtIndexOrFallback_GivenNullCollection_ThrowsNullReferenceException()
84+
public void AtIndexOrFallback_GivenNullCollection_ThrowsArgumentNullException()
8585
{
8686
// Arrange
8787
int[] collection = null;
8888

8989
// Assert
90-
Assert.Throws<NullReferenceException>(() => collection.AtIndexOrFallback(2, 0));
90+
Assert.Throws<ArgumentNullException>(() => collection.AtIndexOrFallback(2, 0));
9191
}
9292

9393
[Fact]

tests/PlainBytes.System.Extensions.Tests/Collections/IteratorsTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void For_GivenNullCollection_ShouldThrow()
2323
_collection = null;
2424

2525
// Assert
26-
Assert.Throws<NullReferenceException>(() => _collection.For((index, element) => { }));
26+
Assert.Throws<ArgumentNullException>(() => _collection.For((index, element) => { }));
2727
}
2828

2929
[Theory]
@@ -71,7 +71,7 @@ public void SelectWithIndex_GivenNullCollection_ShouldThrow()
7171
_collection = null;
7272

7373
// Assert
74-
Assert.Throws<NullReferenceException>(() => _collection.SelectWithIndex((index, element) => element.ToString()).ToList());
74+
Assert.Throws<ArgumentNullException>(() => _collection.SelectWithIndex((index, element) => element.ToString()).ToList());
7575
}
7676

7777
[Theory]
@@ -153,7 +153,7 @@ public void ForEach_GivenNullCollection_ShouldThrow()
153153
_collection = null;
154154

155155
// Assert
156-
Assert.Throws<NullReferenceException>(() => _collection.ForEach(element => { }));
156+
Assert.Throws<ArgumentNullException>(() => _collection.ForEach(element => { }));
157157
}
158158

159159
[Theory]
@@ -182,7 +182,7 @@ public void SelectTypeOf_GivenNullCollection_ShouldThrow()
182182
IEnumerable collection = null;
183183

184184
// Assert
185-
Assert.Throws<NullReferenceException>(() => collection.SelectTypeOf<int>().ToList());
185+
Assert.Throws<ArgumentNullException>(() => collection.SelectTypeOf<int>().ToList());
186186
}
187187

188188
[Fact]
@@ -208,7 +208,7 @@ public void SelectTypeOfGeneric_GivenNullCollection_ShouldThrow()
208208
_collection = null;
209209

210210
// Assert
211-
Assert.Throws<NullReferenceException>(() => _collection.SelectTypeOf<int>().ToList());
211+
Assert.Throws<ArgumentNullException>(() => _collection.SelectTypeOf<int>().ToList());
212212
}
213213

214214
[Fact]

0 commit comments

Comments
 (0)