Skip to content

Commit

Permalink
Adds HasValue (#142)
Browse files Browse the repository at this point in the history
* Ignores Rider

* Adds HasValue which supports null check for nullable value types and objs

* No need to capture, just use the existing value

* Switches implementation

After measuring the boxing path was more performant (both time and alloc)

* Adds more tags to package
  • Loading branch information
danielwertheim authored Jun 4, 2020
1 parent 4684853 commit 6667ce1
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 33 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ packages/
*.orig
*.pfx

# resharper
# resharper & rider
.idea/
_ReSharper.*
*.resharper*
*.[Rr]e[Ss]harper.user

*.DotSettings.user

#windows stuff
Expand Down
24 changes: 24 additions & 0 deletions src/projects/EnsureThat/Enforcers/AnyArg.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
using System;
using EnsureThat.Annotations;
using JetBrains.Annotations;

namespace EnsureThat.Enforcers
{
public sealed class AnyArg
{
/// <summary>
/// Ensures value is not null.
/// Supports both <see cref="Nullable{T}"/> and reference types.
/// If you know you are dealing with a certain type, e.g struct use preferred <see cref="IsNotNull{T}(T?, string, OptsFn)"/>
/// overload instead as it is more performant.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="paramName"></param>
/// <param name="optsFn"></param>
/// <returns></returns>
/// <remarks>If you know you are dealing with e.g. a struct, the <see cref="IsNotNull{T}(T?, string, OptsFn)"/> overload is more performant.</remarks>
[NotNull]
[ContractAnnotation("value:null => halt")]
public T HasValue<T>([NoEnumeration, ValidatedNotNull] T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null)
{
// ReSharper disable once HeapView.BoxingAllocation
if (value == null)
throw Ensure.ExceptionFactory.ArgumentNullException(ExceptionMessages.Common_IsNotNull_Failed, paramName, optsFn);

return value;
}

[NotNull]
[ContractAnnotation("value:null => halt")]
public T IsNotNull<T>([NoEnumeration, ValidatedNotNull] T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : class
Expand Down
31 changes: 31 additions & 0 deletions src/projects/EnsureThat/EnsureArg.Any.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using EnsureThat.Annotations;
using JetBrains.Annotations;

namespace EnsureThat
{
public static partial class EnsureArg
{
/// <summary>
/// Ensures value is not null.
/// Supports both <see cref="Nullable{T}"/> and reference types.
/// If you know you are dealing with a certain type, e.g struct use preferred <see cref="IsNotNull{T}(T?, string, OptsFn)"/>
/// overload instead as it is more performant.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="paramName"></param>
/// <param name="optsFn"></param>
/// <returns></returns>
/// <remarks>If you know you are dealing with e.g. a struct, the <see cref="IsNotNull{T}(T?, string, OptsFn)"/> overload is more performant.</remarks>
[NotNull]
[ContractAnnotation("value:null => halt")]
public static T HasValue<T>([NoEnumeration, ValidatedNotNull] T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null)
=> Ensure.Any.HasValue(value, paramName, optsFn);

[NotNull]
[ContractAnnotation("value:null => halt")]
public static T IsNotNull<T>([NoEnumeration, ValidatedNotNull] T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : class
=> Ensure.Any.IsNotNull(value, paramName, optsFn);
}
}
13 changes: 0 additions & 13 deletions src/projects/EnsureThat/EnsureArg.Objects.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/projects/EnsureThat/EnsureThat.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFrameworks>net4.5.1;netstandard1.1;netstandard2.0</TargetFrameworks>
<Title>Ensure.That</Title>
<PackageId>Ensure.That</PackageId>
<PackageTags>ensure that argument validation guard clause contracts</PackageTags>
<PackageTags>ensure-that ensure that argument validation guard clause contracts</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<AssemblyName>Ensure.That</AssemblyName>
</PropertyGroup>
Expand Down
22 changes: 22 additions & 0 deletions src/projects/EnsureThat/EnsureThatAnyExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace EnsureThat
{
public static class EnsureThatAnyExtensions
{
/// <summary>
/// Ensures value is not null.
/// Supports both <see cref="Nullable{T}"/> and reference types.
/// If you know you are dealing with a certain type, e.g struct use preferred <see cref="EnsureThatValueTypeExtensions.IsNotNull{T}(in Param{T?})"/>
/// overload instead as it is more performant.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="param"></param>
/// <remarks>If you know you are dealing with e.g. a struct, the <see cref="EnsureThatValueTypeExtensions.IsNotNull{T}(in Param{T?})"/> overload is more performant.</remarks>
public static void HasValue<T>(this in Param<T> param)
=> Ensure.Any.HasValue(param.Value, param.Name, param.OptsFn);

public static void IsNotNull<T>(this in Param<T> param) where T : class
=> Ensure.Any.IsNotNull(param.Value, param.Name, param.OptsFn);
}
}
8 changes: 0 additions & 8 deletions src/projects/EnsureThat/EnsureThatObjectExtensions.cs

This file was deleted.

3 changes: 2 additions & 1 deletion src/tests/Benchmarks/Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>7.3</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
55 changes: 47 additions & 8 deletions src/tests/Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ static void Main(string[] args)

public static class BaseLines
{
public static void NullableIntHasValue(int? value)
{
if (!value.HasValue)
throw new ArgumentNullException(nameof(value));
}

public static void StringIsNotNull(string value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
}

public static void StringIsNotNullOrWhiteSpace(string value)
{
if (value == null)
Expand Down Expand Up @@ -78,6 +90,8 @@ public static void ThingsHasItems<T>(List<T> things) where T : class
//[CategoryFilter("Things.HasItems")]
public class Ensures
{
private const string ParamName = "test";
private static readonly int? NullableInt = 1;
private readonly List<string> _strings = new List<string> { "test1", "TEST2", "Test3" };
private readonly List<int> _ints = new List<int> { 1, 2, 3 };
private readonly List<MyThing> _things = new List<MyThing>
Expand Down Expand Up @@ -112,7 +126,7 @@ public void StringIsNotNullOrWhiteSpaceViaThat()
[Benchmark]
[BenchmarkCategory("String.IsNotNullOrWhiteSpace")]
public void StringIsNotNullOrWhiteSpaceViaEnforcer()
=> Ensure.String.IsNotNullOrWhiteSpace("foo", "test");
=> Ensure.String.IsNotNullOrWhiteSpace("foo", ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("String.IsEqualTo")]
Expand All @@ -127,7 +141,7 @@ public void StringIsEqualToViaThat()
[Benchmark]
[BenchmarkCategory("String.IsEqualTo")]
public void StringIsEqualToViaEnforcer()
=> Ensure.String.IsEqualTo("foo", "foo", "test");
=> Ensure.String.IsEqualTo("foo", "foo", ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Strings.HasItems")]
Expand All @@ -142,7 +156,7 @@ public void StringsHasItemsViaThat()
[Benchmark]
[BenchmarkCategory("Strings.HasItems")]
public void StringsHasItemsViaEnforcer()
=> Ensure.Collection.HasItems(_strings, "test");
=> Ensure.Collection.HasItems(_strings, ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Int.Is")]
Expand All @@ -157,7 +171,7 @@ public void IntIsViaThat()
[Benchmark]
[BenchmarkCategory("Int.Is")]
public void IntIsViaEnforcer()
=> Ensure.Comparable.Is(42, 42, "test");
=> Ensure.Comparable.Is(42, 42, ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Int.IsGt")]
Expand All @@ -172,7 +186,7 @@ public void IntIsGtViaThat()
[Benchmark]
[BenchmarkCategory("Int.IsGt")]
public void IntIsGtViaEnforcer()
=> Ensure.Comparable.IsGt(42, 41, "test");
=> Ensure.Comparable.IsGt(42, 41, ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Ints.HasItems")]
Expand All @@ -187,7 +201,7 @@ public void IntsHasItemsViaThat()
[Benchmark]
[BenchmarkCategory("Ints.HasItems")]
public void IntsHasItemsViaEnforcer()
=> Ensure.Collection.HasItems(_ints, "test");
=> Ensure.Collection.HasItems(_ints, ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Any.IsNotNull")]
Expand All @@ -202,7 +216,7 @@ public void ThingIsNotNullViaThat()
[Benchmark]
[BenchmarkCategory("Any.IsNotNull")]
public void ThingIsNotNullViaEnforcer()
=> Ensure.Any.IsNotNull(new MyThing(), "test");
=> Ensure.Any.IsNotNull(new MyThing(), ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Things.HasItems")]
Expand All @@ -217,7 +231,32 @@ public void ThingsHasItemsViaThat()
[Benchmark]
[BenchmarkCategory("Things.HasItems")]
public void ThingsHasItemsViaEnforcer()
=> Ensure.Collection.HasItems(_things, "test");
=> Ensure.Collection.HasItems(_things, ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Any.int.HasValue")]
public void AnyHasValueWhenIntHasValueBaseLine()
=> BaseLines.NullableIntHasValue(NullableInt);

[Benchmark]
[BenchmarkCategory("Any.int.HasValue")]
public void AnyHasValueWhenInt()
=> Ensure.Any.HasValue(NullableInt, ParamName);

[Benchmark]
[BenchmarkCategory("Any.int.HasValue")]
public void AnyHasValueWhenIntViaNotNull()
=> Ensure.Any.IsNotNull(NullableInt, ParamName);

[Benchmark(Baseline = true)]
[BenchmarkCategory("Any.string.HasValue")]
public void AnyHasValueWhenStringBaseLine()
=> BaseLines.StringIsNotNull(string.Empty);

[Benchmark]
[BenchmarkCategory("Any.string.HasValue")]
public void AnyHasValueWhenString()
=> Ensure.Any.HasValue(string.Empty, ParamName);

private class MyThing
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,40 @@

namespace UnitTests
{
public class EnsureObjectParamTests : UnitTestBase

public class EnsureAnyParamTests : UnitTestBase
{
[Fact]
public void HasValue_WhenNull_ThrowsArgumentNullException()
{
static void Verify<T>(T value) =>
ShouldThrow<ArgumentNullException>(
ExceptionMessages.Common_IsNotNull_Failed,
() => Ensure.Any.HasValue(value, ParamName),
() => EnsureArg.HasValue(value, ParamName),
() => Ensure.That(value, ParamName).HasValue());

Verify((int?)null);
Verify((string)null);
Verify((Foo?)null);
Verify((Type)null);
}

[Fact]
public void HasValue_WhenNotNull_ShouldNotThrow()
{
static void Verify<T>(T value) =>
ShouldNotThrow(
() => Ensure.Any.HasValue(value, ParamName),
() => EnsureArg.HasValue(value, ParamName),
() => Ensure.That(value, ParamName).HasValue());

Verify((int?)1);
Verify("");
Verify(Foo.Bar);
Verify(typeof(int));
}

[Fact]
public void IsNotNull_WhenRefTypeIsNull_ThrowsArgumentNullException()
{
Expand Down Expand Up @@ -51,5 +83,10 @@ public void IsNotDefault_WhenIsNotDefault_ShouldNotThrow()
() => EnsureArg.IsNotDefault(value, ParamName),
() => Ensure.That(value, ParamName).IsNotDefault());
}

private enum Foo
{
Bar
}
}
}

0 comments on commit 6667ce1

Please sign in to comment.