Skip to content

Commit

Permalink
Struct equality implemented properly. (#163)
Browse files Browse the repository at this point in the history
All the structs have proper equality overrides and implement IEquatable.
This can save a lot of resources if they're ever used in a hashset,
dictionary etc.
  • Loading branch information
KaliCZ authored Jan 4, 2024
2 parents b0548ec + 1d49a96 commit b7fa1ba
Show file tree
Hide file tree
Showing 32 changed files with 618 additions and 36 deletions.
16 changes: 16 additions & 0 deletions src/FuncSharp.Tests/Numeric/DigitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ internal void AsDigit()
OptionAssert.IsEmpty(char.MaxValue.AsDigit());
}

[Fact]
internal void Equality()
{
Assert.Equal('0'.AsDigit(), '0'.AsDigit());
Assert.Equal('1'.AsDigit(), '1'.AsDigit());
Assert.Equal('2'.AsDigit(), '2'.AsDigit());
Assert.Equal('3'.AsDigit(), '3'.AsDigit());
Assert.Equal('4'.AsDigit(), '4'.AsDigit());

Assert.NotEqual('0'.AsDigit(), '9'.AsDigit());
Assert.NotEqual('1'.AsDigit(), '8'.AsDigit());
Assert.NotEqual('2'.AsDigit(), '7'.AsDigit());
Assert.NotEqual('3'.AsDigit(), '6'.AsDigit());
Assert.NotEqual('4'.AsDigit(), '5'.AsDigit());
}

[Fact]
internal void FilterDigits()
{
Expand Down
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonNegativeDecimalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonNegative(decimal number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonNegative());
}
}

[Property]
internal void Equality(decimal first, decimal second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonNegative();
var secondOption = second.AsNonNegative();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonNegativeIntTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonNegative(int number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonNegative());
}
}

[Property]
internal void Equality(int first, int second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonNegative();
var secondOption = second.AsNonNegative();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonNegativeLongTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonNegative(long number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonNegative());
}
}

[Property]
internal void Equality(long first, long second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonNegative();
var secondOption = second.AsNonNegative();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonNegativeShortTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonNegative(short number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonNegative());
}
}

[Property]
internal void Equality(short first, short second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonNegative();
var secondOption = second.AsNonNegative();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonPositiveDecimalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonPositive(decimal number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonPositive());
}
}

[Property]
internal void Equality(decimal first, decimal second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonPositive();
var secondOption = second.AsNonPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonPositiveIntTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonPositive(int number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonPositive());
}
}

[Property]
internal void Equality(int first, int second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonPositive();
var secondOption = second.AsNonPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonPositiveLongTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonPositive(long number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonPositive());
}
}

[Property]
internal void Equality(long first, long second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonPositive();
var secondOption = second.AsNonPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/NonPositiveShortTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafeNonPositive(short number)
Assert.Throws<ArgumentException>(() => number.AsUnsafeNonPositive());
}
}

[Property]
internal void Equality(short first, short second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsNonPositive();
var secondOption = second.AsNonPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/PositiveDecimalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafePositive(decimal number)
Assert.Throws<ArgumentException>(() => number.AsUnsafePositive());
}
}

[Property]
internal void Equality(decimal first, decimal second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsPositive();
var secondOption = second.AsPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/PositiveIntTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafePositive(int number)
Assert.Throws<ArgumentException>(() => number.AsUnsafePositive());
}
}

[Property]
internal void Equality(int first, int second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsPositive();
var secondOption = second.AsPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/PositiveLongTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafePositive(long number)
Assert.Throws<ArgumentException>(() => number.AsUnsafePositive());
}
}

[Property]
internal void Equality(long first, long second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsPositive();
var secondOption = second.AsPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
13 changes: 13 additions & 0 deletions src/FuncSharp.Tests/Numeric/PositiveShortTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ internal void AsUnsafePositive(short number)
Assert.Throws<ArgumentException>(() => number.AsUnsafePositive());
}
}

[Property]
internal void Equality(short first, short second)
{
var numbersAreEqual = first == second;
var firstOption = first.AsPositive();
var secondOption = second.AsPositive();
var bothOptionsEmpty = firstOption.IsEmpty && secondOption.IsEmpty;
if (!bothOptionsEmpty)
{
Assert.Equal(numbersAreEqual, firstOption == secondOption);
}
}
}
27 changes: 23 additions & 4 deletions src/FuncSharp.Tests/Strings/NonEmptyStringEqualityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,40 @@ public void EqualityTest()

string text = "ASDF123";
NonEmptyString nonEmptyString = NonEmptyString.CreateUnsafe("ASDF123");
NonEmptyString nonEmptyStringWithSameValue = NonEmptyString.CreateUnsafe("ASDF123");
Assert.True(text == nonEmptyString);
Assert.False(text != nonEmptyString);
Assert.True(nonEmptyString == text);
Assert.False(nonEmptyString != text);
Assert.True(nonEmptyString == nonEmptyStringWithSameValue);
Assert.False(nonEmptyString != nonEmptyStringWithSameValue);

Assert.True(text.Equals(nonEmptyString));
Assert.True(nonEmptyString.Equals(text));
Assert.True(nonEmptyString.Equals(nonEmptyStringWithSameValue));

Assert.False(object.Equals(text, nonEmptyString)); // Unfortunately string doesn't override the default Equals method to compare with IEquatable<string> therefore this is false.
Assert.True(object.Equals(nonEmptyString, text));
Assert.True(object.Equals(nonEmptyString, nonEmptyStringWithSameValue));

string differentString = "Text14";
NonEmptyString differentNonEmptyString = NonEmptyString.CreateUnsafe("Totally different text here.");
Assert.False(differentNonEmptyString.Equals(differentString));
Assert.False(differentString.Equals(differentNonEmptyString));
Assert.False(differentNonEmptyString == differentString);
NonEmptyString differentNonEmptyString2 = NonEmptyString.CreateUnsafe("And completely different again.");

Assert.False(differentString == differentNonEmptyString);
Assert.False(object.Equals(differentNonEmptyString, differentString));
Assert.True(differentString != differentNonEmptyString);
Assert.False(differentNonEmptyString == differentString);
Assert.True(differentNonEmptyString != differentString);
Assert.False(differentNonEmptyString == differentNonEmptyString2);
Assert.True(differentNonEmptyString != differentNonEmptyString2);

Assert.False(differentString.Equals(differentNonEmptyString));
Assert.False(differentNonEmptyString.Equals(differentString));
Assert.False(differentNonEmptyString.Equals(differentNonEmptyString2));

Assert.False(object.Equals(differentString, differentNonEmptyString));
Assert.False(object.Equals(differentNonEmptyString, differentString));
Assert.False(object.Equals(differentNonEmptyString, differentNonEmptyString2));

NonEmptyString null1 = null;
string null2 = null;
Expand Down
8 changes: 4 additions & 4 deletions src/FuncSharp/FuncSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>CS1591</NoWarn>
<Version>9.0.1</Version>
<AssemblyVersion>9.0.1</AssemblyVersion>
<FileVersion>9.0.1</FileVersion>
<Version>9.0.2</Version>
<AssemblyVersion>9.0.2</AssemblyVersion>
<FileVersion>9.0.2</FileVersion>
<PackageId>FuncSharp</PackageId>
<Description>A C# library with main purpose to reduce boilerplate code and avoid bugs thanks to stronger typing. Utilizes many concepts from functional programming languages that are also applicable in C#. Originally written by Honza Široký.</Description>
<Authors>Mews, Honza Široký</Authors>
Expand All @@ -13,7 +13,7 @@
<PackageProjectUrl>https://github.com/MewsSystems/FuncSharp</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>Prevented boxing of options when checking for equality.</PackageReleaseNotes>
<PackageReleaseNotes>All the structs now have proper equality implementation.</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/MewsSystems/FuncSharp</RepositoryUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
25 changes: 18 additions & 7 deletions src/FuncSharp/Numeric/Digit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace FuncSharp;

public readonly struct Digit
public readonly struct Digit : IEquatable<Digit>
{
private Digit(byte value)
{
Expand All @@ -13,10 +13,6 @@ private Digit(byte value)
public static implicit operator byte(Digit i) => i.Value;
public static implicit operator int(Digit i) => i.Value;

public static bool operator ==(Digit left, Digit right) => left.Equals(right);

public static bool operator !=(Digit left, Digit right) => !left.Equals(right);

public static Option<Digit> Create(char value)
{
return CreateNullable(value).ToOption();
Expand All @@ -36,9 +32,24 @@ public static Digit CreateUnsafe(char value)
: null;
}

public override bool Equals(object obj) => obj is Digit other && Value == other.Value;
public static bool operator ==(Digit left, Digit right) => left.Equals(right);

public static bool operator !=(Digit left, Digit right) => !left.Equals(right);

public override int GetHashCode() => Value.GetHashCode();
public override bool Equals(object obj)
{
return obj is Digit other && Equals(other);
}

public bool Equals(Digit other)
{
return Value == other.Value;
}

public override int GetHashCode()
{
return Value.GetHashCode();
}

public override string ToString()
{
Expand Down
Loading

0 comments on commit b7fa1ba

Please sign in to comment.