-
-
Save michaelbartnett/5652076 to your computer and use it in GitHub Desktop.
This is free and unencumbered software released into the public domain. | |
Anyone is free to copy, modify, publish, use, compile, sell, or | |
distribute this software, either in source code form or as a compiled | |
binary, for any purpose, commercial or non-commercial, and by any | |
means. | |
In jurisdictions that recognize copyright laws, the author or authors | |
of this software dedicate any and all copyright interest in the | |
software to the public domain. We make this dedication for the benefit | |
of the public at large and to the detriment of our heirs and | |
successors. We intend this dedication to be an overt act of | |
relinquishment in perpetuity of all present and future rights to this | |
software under copyright law. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
For more information, please refer to <http://unlicense.org> |
// ---------------------------------------------------------------------------- | |
// Tuple structs for use in .NET Not-Quite-3.5 (e.g. Unity3D). | |
// | |
// Used Chapter 3 in http://functional-programming.net/ as a starting point. | |
// | |
// Note: .NET 4.0 Tuples are immutable classes so they're *slightly* different. | |
// ---------------------------------------------------------------------------- | |
using System; | |
namespace Eppy | |
{ | |
/// <summary> | |
/// Utility class that simplifies cration of tuples by using | |
/// method calls instead of constructor calls | |
/// </summary> | |
public static class Tuple | |
{ | |
/// <summary> | |
/// Creates a new tuple value with the specified elements. The method | |
/// can be used without specifying the generic parameters, because C# | |
/// compiler can usually infer the actual types. | |
/// </summary> | |
/// <param name="item1">First element of the tuple</param> | |
/// <param name="second">Second element of the tuple</param> | |
/// <returns>A newly created tuple</returns> | |
public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 second) | |
{ | |
return new Tuple<T1, T2>(item1, second); | |
} | |
/// <summary> | |
/// Creates a new tuple value with the specified elements. The method | |
/// can be used without specifying the generic parameters, because C# | |
/// compiler can usually infer the actual types. | |
/// </summary> | |
/// <param name="item1">First element of the tuple</param> | |
/// <param name="second">Second element of the tuple</param> | |
/// <param name="third">Third element of the tuple</param> | |
/// <returns>A newly created tuple</returns> | |
public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 second, T3 third) | |
{ | |
return new Tuple<T1, T2, T3>(item1, second, third); | |
} | |
/// <summary> | |
/// Creates a new tuple value with the specified elements. The method | |
/// can be used without specifying the generic parameters, because C# | |
/// compiler can usually infer the actual types. | |
/// </summary> | |
/// <param name="item1">First element of the tuple</param> | |
/// <param name="second">Second element of the tuple</param> | |
/// <param name="third">Third element of the tuple</param> | |
/// <param name="fourth">Fourth element of the tuple</param> | |
/// <returns>A newly created tuple</returns> | |
public static Tuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 item1, T2 second, T3 third, T4 fourth) | |
{ | |
return new Tuple<T1, T2, T3, T4>(item1, second, third, fourth); | |
} | |
/// <summary> | |
/// Extension method that provides a concise utility for unpacking | |
/// tuple components into specific out parameters. | |
/// </summary> | |
/// <param name="tuple">the tuple to unpack from</param> | |
/// <param name="ref1">the out parameter that will be assigned tuple.Item1</param> | |
/// <param name="ref2">the out parameter that will be assigned tuple.Item2</param> | |
public static void Unpack<T1, T2>(this Tuple<T1, T2> tuple, out T1 ref1, out T2 ref2) | |
{ | |
ref1 = tuple.Item1; | |
ref2 = tuple.Item2; | |
} | |
/// <summary> | |
/// Extension method that provides a concise utility for unpacking | |
/// tuple components into specific out parameters. | |
/// </summary> | |
/// <param name="tuple">the tuple to unpack from</param> | |
/// <param name="ref1">the out parameter that will be assigned tuple.Item1</param> | |
/// <param name="ref2">the out parameter that will be assigned tuple.Item2</param> | |
/// <param name="ref3">the out parameter that will be assigned tuple.Item3</param> | |
public static void Unpack<T1, T2, T3>(this Tuple<T1, T2, T3> tuple, out T1 ref1, out T2 ref2, T3 ref3) | |
{ | |
ref1 = tuple.Item1; | |
ref2 = tuple.Item2; | |
ref3 = tuple.Item3; | |
} | |
/// <summary> | |
/// Extension method that provides a concise utility for unpacking | |
/// tuple components into specific out parameters. | |
/// </summary> | |
/// <param name="tuple">the tuple to unpack from</param> | |
/// <param name="ref1">the out parameter that will be assigned tuple.Item1</param> | |
/// <param name="ref2">the out parameter that will be assigned tuple.Item2</param> | |
/// <param name="ref3">the out parameter that will be assigned tuple.Item3</param> | |
/// <param name="ref4">the out parameter that will be assigned tuple.Item4</param> | |
public static void Unpack<T1, T2, T3, T4>(this Tuple<T1, T2, T3, T4> tuple, out T1 ref1, out T2 ref2, T3 ref3, T4 ref4) | |
{ | |
ref1 = tuple.Item1; | |
ref2 = tuple.Item2; | |
ref3 = tuple.Item3; | |
ref4 = tuple.Item4; | |
} | |
} | |
} |
// ---------------------------------------------------------------------------- | |
// Tuple structs for use in .NET Not-Quite-3.5 (e.g. Unity3D). | |
// | |
// Used Chapter 3 in http://functional-programming.net/ as a starting point. | |
// | |
// Note: .NET 4.0 Tuples are immutable classes so they're *slightly* different. | |
// ---------------------------------------------------------------------------- | |
using System; | |
namespace Eppy | |
{ | |
/// <summary> | |
/// Represents a functional tuple that can be used to store | |
/// two values of different types inside one object. | |
/// </summary> | |
/// <typeparam name="T1">The type of the first element</typeparam> | |
/// <typeparam name="T2">The type of the second element</typeparam> | |
public sealed class Tuple<T1, T2> | |
{ | |
private readonly T1 item1; | |
private readonly T2 item2; | |
/// <summary> | |
/// Retyurns the first element of the tuple | |
/// </summary> | |
public T1 Item1 | |
{ | |
get { return item1; } | |
} | |
/// <summary> | |
/// Returns the second element of the tuple | |
/// </summary> | |
public T2 Item2 | |
{ | |
get { return item2; } | |
} | |
/// <summary> | |
/// Create a new tuple value | |
/// </summary> | |
/// <param name="item1">First element of the tuple</param> | |
/// <param name="second">Second element of the tuple</param> | |
public Tuple(T1 item1, T2 item2) | |
{ | |
this.item1 = item1; | |
this.item2 = item2; | |
} | |
public override string ToString() | |
{ | |
return string.Format("Tuple({0}, {1})", Item1, Item2); | |
} | |
public override int GetHashCode() | |
{ | |
int hash = 17; | |
hash = hash * 23 + (item1 == null ? 0 : item1.GetHashCode()); | |
hash = hash * 23 + (item2 == null ? 0 : item2.GetHashCode()); | |
return hash; | |
} | |
public override bool Equals(object o) | |
{ | |
if (!(o is Tuple<T1, T2>)) { | |
return false; | |
} | |
var other = (Tuple<T1, T2>) o; | |
return this == other; | |
} | |
public bool Equals(Tuple<T1, T2> other) | |
{ | |
return this == other; | |
} | |
public static bool operator==(Tuple<T1, T2> a, Tuple<T1, T2> b) | |
{ | |
if (object.ReferenceEquals(a, null)) { | |
return object.ReferenceEquals(b, null); | |
} | |
if (a.item1 == null && b.item1 != null) return false; | |
if (a.item2 == null && b.item2 != null) return false; | |
return | |
a.item1.Equals(b.item1) && | |
a.item2.Equals(b.item2); | |
} | |
public static bool operator!=(Tuple<T1, T2> a, Tuple<T1, T2> b) | |
{ | |
return !(a == b); | |
} | |
public void Unpack(Action<T1, T2> unpackerDelegate) | |
{ | |
unpackerDelegate(Item1, Item2); | |
} | |
} | |
} |
// ---------------------------------------------------------------------------- | |
// Tuple structs for use in .NET Not-Quite-3.5 (e.g. Unity3D). | |
// | |
// Used Chapter 3 in http://functional-programming.net/ as a starting point. | |
// | |
// Note: .NET 4.0 Tuples are immutable classes so they're *slightly* different. | |
// ---------------------------------------------------------------------------- | |
using System; | |
namespace Eppy | |
{ | |
/// <summary> | |
/// Represents a functional tuple that can be used to store | |
/// two values of different types inside one object. | |
/// </summary> | |
/// <typeparam name="T1">The type of the first element</typeparam> | |
/// <typeparam name="T2">The type of the second element</typeparam> | |
/// <typeparam name="T3">The type of the third element</typeparam> | |
public sealed class Tuple<T1, T2, T3> | |
{ | |
private readonly T1 item1; | |
private readonly T2 item2; | |
private readonly T3 item3; | |
/// <summary> | |
/// Retyurns the first element of the tuple | |
/// </summary> | |
public T1 Item1 | |
{ | |
get { return item1; } | |
} | |
/// <summary> | |
/// Returns the second element of the tuple | |
/// </summary> | |
public T2 Item2 | |
{ | |
get { return item2; } | |
} | |
/// <summary> | |
/// Returns the second element of the tuple | |
/// </summary> | |
public T3 Item3 | |
{ | |
get { return item3; } | |
} | |
/// <summary> | |
/// Create a new tuple value | |
/// </summary> | |
/// <param name="item1">First element of the tuple</param> | |
/// <param name="second">Second element of the tuple</param> | |
/// <param name="third">Third element of the tuple</param> | |
public Tuple(T1 item1, T2 item2, T3 item3) | |
{ | |
this.item1 = item1; | |
this.item2 = item2; | |
this.item3 = item3; | |
} | |
public override int GetHashCode() | |
{ | |
int hash = 17; | |
hash = hash * 23 + (item1 == null ? 0 : item1.GetHashCode()); | |
hash = hash * 23 + (item2 == null ? 0 : item2.GetHashCode()); | |
hash = hash * 23 + (item3 == null ? 0 : item3.GetHashCode()); | |
return hash; | |
} | |
public override bool Equals(object o) | |
{ | |
if (!(o is Tuple<T1, T2, T3>)) { | |
return false; | |
} | |
var other = (Tuple<T1, T2, T3>)o; | |
return this == other; | |
} | |
public static bool operator==(Tuple<T1, T2, T3> a, Tuple<T1, T2, T3> b) | |
{ | |
if (object.ReferenceEquals(a, null)) { | |
return object.ReferenceEquals(b, null); | |
} | |
if (a.item1 == null && b.item1 != null) return false; | |
if (a.item2 == null && b.item2 != null) return false; | |
if (a.item3 == null && b.item3 != null) return false; | |
return | |
a.item1.Equals(b.item1) && | |
a.item2.Equals(b.item2) && | |
a.item3.Equals(b.item3); | |
} | |
public static bool operator!=(Tuple<T1, T2, T3> a, Tuple<T1, T2, T3> b) | |
{ | |
return !(a == b); | |
} | |
public void Unpack(Action<T1, T2, T3> unpackerDelegate) | |
{ | |
unpackerDelegate(Item1, Item2, Item3); | |
} | |
} | |
} |
// ---------------------------------------------------------------------------- | |
// Tuple structs for use in .NET Not-Quite-3.5 (e.g. Unity3D). | |
// | |
// Used Chapter 3 in http://functional-programming.net/ as a starting point. | |
// | |
// Note: .NET 4.0 Tuples are immutable classes so they're *slightly* different. | |
// ---------------------------------------------------------------------------- | |
using System; | |
namespace Eppy | |
{ | |
/// <summary> | |
/// Represents a functional tuple that can be used to store | |
/// two values of different types inside one object. | |
/// </summary> | |
/// <typeparam name="T1">The type of the first element</typeparam> | |
/// <typeparam name="T2">The type of the second element</typeparam> | |
/// <typeparam name="T3">The type of the third element</typeparam> | |
/// <typeparam name="T4">The type of the fourth element</typeparam> | |
public sealed class Tuple<T1, T2, T3, T4> | |
{ | |
private readonly T1 item1; | |
private readonly T2 item2; | |
private readonly T3 item3; | |
private readonly T4 item4; | |
/// <summary> | |
/// Retyurns the first element of the tuple | |
/// </summary> | |
public T1 Item1 | |
{ | |
get { return item1; } | |
} | |
/// <summary> | |
/// Returns the second element of the tuple | |
/// </summary> | |
public T2 Item2 | |
{ | |
get { return item2; } | |
} | |
/// <summary> | |
/// Returns the second element of the tuple | |
/// </summary> | |
public T3 Item3 | |
{ | |
get { return item3; } | |
} | |
/// <summary> | |
/// Returns the second element of the tuple | |
/// </summary> | |
public T4 Item4 | |
{ | |
get { return item4; } | |
} | |
/// <summary> | |
/// Create a new tuple value | |
/// </summary> | |
/// <param name="item1">First element of the tuple</param> | |
/// <param name="second">Second element of the tuple</param> | |
/// <param name="third">Third element of the tuple</param> | |
/// <param name="fourth">Fourth element of the tuple</param> | |
public Tuple(T1 item1, T2 item2, T3 item3, T4 item4) | |
{ | |
this.item1 = item1; | |
this.item2 = item2; | |
this.item3 = item3; | |
this.item4 = item4; | |
} | |
public override int GetHashCode() | |
{ | |
int hash = 17; | |
hash = hash * 23 + (item1 == null ? 0 : item1.GetHashCode()); | |
hash = hash * 23 + (item2 == null ? 0 : item2.GetHashCode()); | |
hash = hash * 23 + (item3 == null ? 0 : item3.GetHashCode()); | |
hash = hash * 23 + (item4 == null ? 0 : item4.GetHashCode()); | |
return hash; | |
} | |
public override bool Equals(object o) | |
{ | |
if (o.GetType() != typeof(Tuple<T1, T2, T3, T4>)) { | |
return false; | |
} | |
var other = (Tuple<T1, T2, T3, T4>)o; | |
return this == other; | |
} | |
public static bool operator==(Tuple<T1, T2, T3, T4> a, Tuple<T1, T2, T3, T4> b) | |
{ | |
if (object.ReferenceEquals(a, null)) { | |
return object.ReferenceEquals(b, null); | |
} | |
if (a.item1 == null && b.item1 != null) return false; | |
if (a.item2 == null && b.item2 != null) return false; | |
if (a.item3 == null && b.item3 != null) return false; | |
if (a.item4 == null && b.item4 != null) return false; | |
return | |
a.item1.Equals(b.item1) && | |
a.item2.Equals(b.item2) && | |
a.item3.Equals(b.item3) && | |
a.item4.Equals(b.item4); | |
} | |
public static bool operator!=(Tuple<T1, T2, T3, T4> a, Tuple<T1, T2, T3, T4> b) | |
{ | |
return !(a == b); | |
} | |
public void Unpack(Action<T1, T2, T3, T4> unpackerDelegate) | |
{ | |
unpackerDelegate(Item1, Item2, Item3, Item4); | |
} | |
} | |
} |
Thanks for the classes!
The code for operator== isn't quite correct. Something like
if (myTuple == null)
will throw an exception, because the second parameter ("b") isn't null-checked in the code.
Here's one way to fix it (replaces the ReferenceEquals calls at the top of the method):
bool aIsNull = object.ReferenceEquals(a, null);
bool bIsNull = object.ReferenceEquals(b, null);
if (aIsNull || bIsNull)
{
return aIsNull && bIsNull;
}
I'm sure there are more elegant ways, but this one works and is readable.
Thanks again for the great classes!
Even with kmeboe's fix, this still breaks:
var a = Tuple<string, string>("1", null);
var b = Tuple<string, string>("1", null);
Debug.Log(a == b); // NullReferenceException
My first pass at a solution that fixes all null cases was this:
public static bool operator ==(Tuple<T1, T2> a, Tuple<T1, T2> b)
{
// Handle a or b being null
bool bIsNull = ReferenceEquals(b, null);
// a is null, equality depends on b being null
if (ReferenceEquals(a, null))
return bIsNull;
// a NOT null, b is null, so NOT EQUAL
else if (bIsNull)
return false;
// a.1 is null.
if (ReferenceEquals(a.item1, null))
{
// We only know NOT EQUAL if b.1 is not null
if (!ReferenceEquals(b.item1, null))
return false;
}
// a.1 is not null, so NOT EQUAL if a.1 != b.1
else if (!a.item1.Equals(b.item1))
return false;
// a.2 is null.
if (ReferenceEquals(a.item2, null))
{
// We only know NOT EQUAL if b2 is not null
if (!ReferenceEquals(b.item2, null))
return false;
}
// a.2 is not null, so NOT EQUAL if a.2 != b.2
else if (!a.item2.Equals(b.item2))
return false;
// Everything was equal
return true;
}
However, if we can use object.Equals(obj a, obj b)
then it can be simplified to just:
public static bool operator ==(Tuple<T1, T2> a, Tuple<T1, T2> b)
{
// Handle a or b being null
bool bIsNull = ReferenceEquals(b, null);
// a is null, equaliy depends on b being null
if (ReferenceEquals(a, null))
return bIsNull;
// a NOT null, b is null, so NOT EQUAL
else if (bIsNull)
return false;
// Handle Items
return object.Equals(a.item1, b.item1) && object.Equals(a.item2, b.item2);
}
I'm unsure if there might be a reason we don't want to use object.Equals
for the items. Thoughts?
In any case, I believe either of these solutions handle all null permutations. To verify for yourself, here is a simple test:
static void RunTest()
{
Tuple<string, string>[] list =
{
new Tuple<string, string>("1", "2"),
new Tuple<string, string>("1", null),
new Tuple<string, string>(null, "2"),
new Tuple<string, string>(null, null),
null // just to make sure we are checking for null objects
};
foreach(var lhs in list)
{
// Check for same values, different object
if(lhs != null)
{
DoCompare(lhs, Tuple.Create(lhs.Item1, lhs.Item2));
DoCompare(Tuple.Create(lhs.Item1, lhs.Item2), lhs);
}
// Check all pairings
foreach(var rhs in list)
DoCompare(lhs, rhs);
}
}
static void DoCompare(Tuple<string, string> lhs, Tuple<string, string> rhs)
{
Debug.LogFormat("{0} == {1} : {2}",
lhs == null ? "NULL" : lhs.ToString(),
rhs == null ? "NULL" : rhs.ToString(),
(lhs == rhs)
);
}
It should be noted that this code can be simplified considerably if you don't expect your tuples to contain null values. I personally don't feel comfortable making that assumption.
@michaelbartnett Thanks for the tuples! They've been exceptionally useful.
I believe there is a bug in Tuple4 Equals.
original:
if (o.GetType() != typeof(NamedTuple<T1, T2, T3, T4>)) {
return false;
}
fix:
if (!(o is NamedTuple<T1, T2, T3, T4>)) {
return false;
}
Aren't == operators generally suppose to test for reference equality and Equals methods for value equality?
Edit: Ah ok, so jjcat posted proposed separation of function for those so I suppose this wasn't meant to be like that