12

(I would check this out for myself, but I don't have VS2010 (yet))

Say I have 2 base interfaces:

IBaseModelInterface
IBaseViewInterface

And 2 interfaces realizing those:

ISubModelInterface : IBaseModelInterface
ISubViewInterface : IBaseViewInterface

If I define a Tuple<IBaseModelInterface, IBaseViewInterface> I would like to set that based on the result of a factory that returns Tuple<ISubModelInterface, ISubViewInterface>.

In C# 3 I can't do this even though the sub interfaces realize the base interfaces. And I'm pretty sure C# 4 lets me do this if I was using IEnumerable<IBaseModelInterface> because it's now defined with the in keyword to allow covariance. So does Tuple allow me to do this?

From what (little) I understand, covariance is only allowed on interfaces, so does that mean there needs to be an ITuple<T1, T2> interface? Does this exist?

RichK
  • 11,318
  • 6
  • 35
  • 49
  • 4
    Note that the "in" keyword allows *contravariance*, not *covariance*. IEnumerable is marked as *out* because *a T comes out of an IEnumerable*. – Eric Lippert May 20 '10 at 14:16

2 Answers2

12

Tuple is a class (well, a family of classes) - it's invariant by definition. As you mention later on, only interfaces and delegate types support generic variance in .NET 4.

There's no ITuple interface that I'm aware of. There could be one which would be covariant, as the tuples are immutable so you only get values "out" of the API.

Paul Rowland
  • 8,244
  • 12
  • 55
  • 76
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I guess I could define my own IMyTuple that'd be neat – RichK May 20 '10 at 11:15
  • 1
    @RichK - if you do intend to write your own tuple class, you should carefully examine the comparison and equality semantics offered by BCL tuples. I assume you would want to preserve the same behavior as offered in the built in classes - and some of that behavior is non-obvious. – LBushkin May 20 '10 at 13:28
  • 2
    @Jon Skeet, there is a nongeneric `ITuple` interface, but it's internal – smartcaveman Nov 09 '12 at 15:23
  • @smartcaveman: Yes, there is, `TRest` will be checked if it is `ITuple`. – Ken Kin Aug 24 '13 at 04:20
9

You can inherit from tuple for create your own Covariant Tuple. This way you avoid to have to rewrite your own equalities logic.

public interface ICovariantTuple<out T1>
{
    T1 Item1 { get; }
}
public class CovariantTuple<T1> : Tuple<T1>, ICovariantTuple<T1>
{
    public CovariantTuple(T1 item1) : base(item1) { }
}

public interface ICovariantTuple<out T1, out T2>
{
    T1 Item1 { get; }
    T2 Item2 { get; }
}
public class CovariantTuple<T1, T2> : Tuple<T1, T2>, ICovariantTuple<T1, T2>
{
    public CovariantTuple(T1 item1, T2 item2) : base(item1, item2) { }
}

etc.... for 3, 4, 5, 6, 7, 8 items

Compile Fail

Tuple<Exception> item = new Tuple<ArgumentNullException>(null);

Compile Success

ICovariantTuple<Exception> item = new CovariantTuple<ArgumentNullException>(null);

There is no base Tuple after 8 items, but it should be enough.

Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
  • I like your solution with the interface and companion class! But what's the purpose of the empty factory? I think the last two examples show exactly what works and not. – MEMark Feb 26 '14 at 18:40