I was searching for a way to put a Tuple<List>
into a Tuple<IEnumerable>
, and I found that Tuple has no covariance.
I decide to make my own implementation of an interface which subclass a tuple with a support for Covariance:
/// <summary>
/// A wrapper around Tuple for gaining covariance
/// </summary>
public static class ITuple
{
private class _ITuple<T1> : Tuple<T1>, ITuple<T1> { public _ITuple(T1 item1) : base(item1) { } }
public static ITuple<T1> Create<T1>(T1 item1) { return new _ITuple<T1>(item1); }
private class _ITuple<T1, T2> : Tuple<T1, T2>, ITuple<T1, T2> { public _ITuple(T1 item1, T2 item2) : base(item1, item2) { } }
public static ITuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) { return new _ITuple<T1, T2>(item1, item2); }
private class _ITuple<T1, T2, T3> : Tuple<T1, T2, T3>, ITuple<T1, T2, T3> { public _ITuple(T1 item1, T2 item2, T3 item3) : base(item1, item2, item3) { } }
public static ITuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3) { return new _ITuple<T1, T2, T3>(item1, item2, item3); }
}
public interface ITuple<out T1> { T1 Item1 { get; } }
public interface ITuple<out T1, out T2> { T1 Item1 { get; } T2 Item2 { get; } }
public interface ITuple<out T1, out T2, out T3> { T1 Item1 { get; } T2 Item2 { get; } T3 Item3 { get; } }
Some tests and usage:
[TestMethod]
public void TestCompile()
{
// no assertion, just test that the compilation work
ITuple<Exception> item = ITuple.Create<NullReferenceException>(null);
ITuple<IEnumerable<int>> item2 = ITuple.Create<List<int>>(null);
}
[TestMethod]
public void TestEqual()
{
Assert.AreEqual(ITuple.Create(1, Tuple.Create("a")), ITuple.Create(1, Tuple.Create("a")));
Assert.AreNotEqual(ITuple.Create(1, Tuple.Create("a")), ITuple.Create(1, Tuple.Create("b")));
}
I choose to hide the implementation of the concrete class _ITuple
inside the static class, so you have access only to the public and static builder ITuple.Create
.
Problems:
Subclass and equality support
Subclassing the tuple is easy and there is nothing to do, and this is my concern. Is there something am I missing? I see no reason why equalities and hashcode souldn't work as expected with classic Tuple, but maybe you will find a problem somewhere which I didn't think of.
Naming convention
- Like the static class
Tuple
contains all theCreate
static methods,ITuple
, which is not an interface, has the static builder. At what point is this weird to name a non interface with anI
prefix? - At first, I called the
ITuple
interfaceICovariantTuple
, but found it too long, and there is noITuple
in the framework. Is this evident thatITuple
is covariant?
- Like the static class