Skip to content

Commit

Permalink
Merge branch 'MattKotsenas-icomparable' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
nlkl committed Aug 13, 2017
2 parents 8da48a1 + 8f06db6 commit 6cfd690
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ _TeamCity*
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
*.ncrunchproject
*.ncrunchsolution

# MightyMoose
*.mm.*
Expand Down
43 changes: 43 additions & 0 deletions src/Optional.Tests/EitherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,49 @@ public void Either_Equality()
Assert.IsTrue(Option.Some<int, int>(22) != Option.Some<int, int>(21));
}

[TestMethod]
public void Either_CompareTo()
{
// Value type comparisons
var noneStruct = Option.None<int, string>("none");
var someStruct1 = Option.Some<int, string>(1);
var someStruct2 = Option.Some<int, string>(2);

Assert.AreEqual(someStruct1, new[] { someStruct1, someStruct2 }.Min());
Assert.AreEqual(noneStruct, new[] { noneStruct, someStruct1 }.Min());
Assert.AreEqual(noneStruct, new[] { noneStruct, noneStruct }.Min());

Assert.AreEqual(someStruct2, new[] { someStruct1, someStruct2 }.Max());
Assert.AreEqual(someStruct1, new[] { noneStruct, someStruct1 }.Max());
Assert.AreEqual(noneStruct, new[] { noneStruct, noneStruct }.Max());

// IComparable comparisons
var noneComparable = Option.None<string, int>(0);
var someComparable1 = Option.Some<string, int>("1");
var someComparable2 = Option.Some<string, int>("2");

Assert.AreEqual(someComparable1, new[] { someComparable1, someComparable2 }.Min());
Assert.AreEqual(noneComparable, new[] { noneComparable, someComparable1 }.Min());
Assert.AreEqual(noneComparable, new[] { noneComparable, noneComparable }.Min());

Assert.AreEqual(someComparable2, new[] { someComparable1, someComparable2 }.Max());
Assert.AreEqual(someComparable1, new[] { noneComparable, someComparable1 }.Max());
Assert.AreEqual(noneComparable, new[] { noneComparable, noneComparable }.Max());

// Non-IComparable comparisons
var noneNotComparable = Option.None<Dictionary<string, string>, string>("none");
var someNotComparable1 = Option.Some<Dictionary<string, string>, string>(new Dictionary<string, string>());
var someNotComparable2 = Option.Some<Dictionary<string, string>, string>(new Dictionary<string, string>());

Assert.ThrowsException<ArgumentException>(() => new[] { someNotComparable1, someNotComparable2 }.Min());
Assert.AreEqual(noneNotComparable, new[] { noneNotComparable, someNotComparable1 }.Min());
Assert.AreEqual(noneNotComparable, new[] { noneNotComparable, noneNotComparable }.Min());

Assert.ThrowsException<ArgumentException>(() => new[] { someNotComparable1, someNotComparable2 }.Max());
Assert.AreEqual(someNotComparable1, new[] { noneNotComparable, someNotComparable1 }.Max());
Assert.AreEqual(noneNotComparable, new[] { noneNotComparable, noneNotComparable }.Max());
}

[TestMethod]
public void Either_Hashing()
{
Expand Down
43 changes: 43 additions & 0 deletions src/Optional.Tests/MaybeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,49 @@ public void Maybe_Equality()
Assert.IsTrue(Option.Some(22) != Option.Some(21));
}

[TestMethod]
public void Maybe_CompareTo()
{
// Value type comparisons
var noneStruct = Option.None<int>();
var someStruct1 = Option.Some<int>(1);
var someStruct2 = Option.Some<int>(2);

Assert.AreEqual(someStruct1, new[] {someStruct1, someStruct2}.Min());
Assert.AreEqual(noneStruct, new[] {noneStruct, someStruct1}.Min());
Assert.AreEqual(noneStruct, new[] {noneStruct, noneStruct}.Min());

Assert.AreEqual(someStruct2, new[] {someStruct1, someStruct2}.Max());
Assert.AreEqual(someStruct1, new[] {noneStruct, someStruct1}.Max());
Assert.AreEqual(noneStruct, new[] {noneStruct, noneStruct}.Max());

// IComparable comparisons
var noneComparable = Option.None<string>();
var someComparable1 = Option.Some<string>("1");
var someComparable2 = Option.Some<string>("2");

Assert.AreEqual(someComparable1, new[] {someComparable1, someComparable2}.Min());
Assert.AreEqual(noneComparable, new[] {noneComparable, someComparable1}.Min());
Assert.AreEqual(noneComparable, new[] {noneComparable, noneComparable}.Min());

Assert.AreEqual(someComparable2, new[] {someComparable1, someComparable2}.Max());
Assert.AreEqual(someComparable1, new[] {noneComparable, someComparable1}.Max());
Assert.AreEqual(noneComparable, new[] {noneComparable, noneComparable}.Max());

// Non-IComparable comparisons
var noneNotComparable = Option.None<Dictionary<string, string>>();
var someNotComparable1 = Option.Some<Dictionary<string, string>>(new Dictionary<string, string>());
var someNotComparable2 = Option.Some<Dictionary<string, string>>(new Dictionary<string, string>());

Assert.ThrowsException<ArgumentException>(() => new[] {someNotComparable1, someNotComparable2}.Min());
Assert.AreEqual(noneNotComparable, new[] {noneNotComparable, someNotComparable1}.Min());
Assert.AreEqual(noneNotComparable, new[] {noneNotComparable, noneNotComparable}.Min());

Assert.ThrowsException<ArgumentException>(() => new[] {someNotComparable1, someNotComparable2}.Max());
Assert.AreEqual(someNotComparable1, new[] {noneNotComparable, someNotComparable1}.Max());
Assert.AreEqual(noneNotComparable, new[] {noneNotComparable, noneNotComparable}.Max());
}

[TestMethod]
public void Maybe_Hashing()
{
Expand Down
35 changes: 34 additions & 1 deletion src/Optional/Option_Either.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Optional
#if !NETSTANDARD10
[Serializable]
#endif
public struct Option<T, TException> : IEquatable<Option<T, TException>>
public struct Option<T, TException> : IEquatable<Option<T, TException>>, IComparable<Option<T, TException>>
{
private readonly bool hasValue;
private readonly T value;
Expand Down Expand Up @@ -100,6 +100,39 @@ public override int GetHashCode()
return exception.GetHashCode();
}


/// <summary>
/// Compares the current instance with another object of the same type and returns an
/// integer that indicates whether the current instance precedes, follows, or occurs in
/// the same position in the sort order as the other object.
/// </summary>
/// <param name="other">An object to compare with this instance.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
public int CompareTo(Option<T, TException> other)
{
if (!hasValue && !other.hasValue)
{
// If both have exceptions, sort by the exceptions
return Comparer<TException>.Default.Compare(exception, other.exception);
}
else if (hasValue && other.hasValue)
{
// If both have values, sort by the values
return Comparer<T>.Default.Compare(value, other.value);
}

// If one has an exception and the other has a value, sort the exceptions before the values
// to maintain the ComapresTo contract, which sorts nulls before values
if (!hasValue)
{
return -1;
}
else
{
return 1;
}
}

/// <summary>
/// Returns a string that represents the current optional.
/// </summary>
Expand Down
14 changes: 13 additions & 1 deletion src/Optional/Option_Maybe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Optional
#if !NETSTANDARD10
[Serializable]
#endif
public struct Option<T> : IEquatable<Option<T>>
public struct Option<T> : IEquatable<Option<T>>, IComparable<Option<T>>
{
private readonly bool hasValue;
private readonly T value;
Expand Down Expand Up @@ -91,6 +91,18 @@ public override int GetHashCode()
return 0;
}

/// <summary>
/// Compares the current instance with another object of the same type and returns an
/// integer that indicates whether the current instance precedes, follows, or occurs in
/// the same position in the sort order as the other object.
/// </summary>
/// <param name="other">An object to compare with this instance.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
public int CompareTo(Option<T> other)
{
return Comparer<T>.Default.Compare(value, other.value);
}

/// <summary>
/// Returns a string that represents the current optional.
/// </summary>
Expand Down

0 comments on commit 6cfd690

Please sign in to comment.