I was searching for a way to put a Tuple<List> into a Tuple<IEnumerable>, and I found that Tuple has no covariance.

  1. Is C# 4.0 Tuple covariant
  2. Co/Contravariance? Tuple> not satisfied with List

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:

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);
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.


  1. 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.

  2. Naming convention

    • Like the static class Tuple contains all the Create static methods, ITuple, which is not an interface, has the static builder. At what point is this weird to name a non interface with an I prefix?
    • At first, I called the ITuple interface ICovariantTuple, but found it too long, and there is no ITuple in the framework. Is this evident that ITuple is covariant?

1 Answer 1

  1. I think that non-interface types shouldn't be called ISomething. You could just call the static class Tuple and differentiate it from System.Tuple by using a different namespace. Or you could call the class something like TupleEx, though that's not very descriptive.

    But the best option is if you could figure out a good name for the type that differentiates it from System.Tuple. I agree that CovariantTuple is quite long, though I don't have any better ideas.

  2. I also don't like the name _ITuple, even if it's not publicly visible. I would call it something like TupleImpl. (Again, if you figure out a good name, you could use that here too.)

  3. Regarding equality: your ITuples will show as equal to normal Tuples with the same contents. This most likely is what you want, but you should be aware of it.

    I don't think there are any other problems with equality or hash codes.

  4. Consider not writing everything on a single line. I think it will make the code clearer.

  5. It might make sense to add some way to convert System.Tuple to ITuple and back.

  • 1
    \$\begingroup\$ +1, points 1 and 5 possibly lead us to a TupleFactory with methods like Create() and CreateCovariant()? \$\endgroup\$
    – MattDavey
    Nov 18, 2013 at 17:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.