Constraining class generic type to a Tuple

.net, c#, c#-4.0, generics

Solution

You can't as others have stated, but you can almost do it.

So all the `Tuple<...>` classes have a signature like this:

public class Tuple<T1, ...> : 
    IStructuralEquatable, 
    IStructuralComparable, 
    IComparable, 
    ITuple

All of those interfaces save `ITuple` are public (`ITuple` is an internal interface), so you can try crafting something like so:

public interface ITupleKey<TKey>
    where TKey : IStructuralEquatable, IStructuralComparable, IComparable
{
}

"But wait!", you say, "How can I be sure nothing else is implementing those interfaces?"

Well, you can't. But like I said, this is just an almost way - luckily, `IStructuralEquatable` and `IStructuralComparable` are only (at the framework level, naturally) used in the following types:

System.Array
System.Tuple<T1>
System.Tuple<T1,T2>
System.Tuple<T1,T2,T3>
System.Tuple<T1,T2,T3,T4>
System.Tuple<T1,T2,T3,T4,T5>
System.Tuple<T1,T2,T3,T4,T5,T6>
System.Tuple<T1,T2,T3,T4,T5,T6,T7>
System.Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>

So it's pretty close. Combine this with a runtime check that `TKey` actually is some variant of `Tuple`, and you might have what you need.

EDIT:

Some basic usage:

public class Class1<TKey> 
    where TKey : IStructuralEquatable, IStructuralComparable, IComparable
{ 
}

// will compile
var classTup1 = new Class1<Tuple<int>>();
var classTup2 = new Class1<Tuple<int,int>>();
var classTup3 = new Class1<Tuple<int,int,int>>();
var classTup4 = new Class1<Tuple<int,int,int,int>>();
var classTup5 = new Class1<Tuple<int,int,int,int,int>>();

// won't compile
var badclassTup1 = new Class1<int>();
var badclassTup2 = new Class1<string>();
var badclassTup3 = new Class1<object>();

And, because I've clearly gone insane, let's see what's possible here:

public class Class1<TKey> 
    where TKey : IStructuralEquatable, IStructuralComparable, IComparable
{ 
    public Class1(TKey key)
    {
        Key = key;
        TupleRank = typeof(TKey).GetGenericArguments().Count();
        TupleSubtypes = typeof(TKey).GetGenericArguments();
        Console.WriteLine("Key type is a Tuple (I think) with {0} elements", TupleRank);
        TupleGetters = 
            Enumerable.Range(1, TupleRank)
                .Select(i => typeof(TKey).GetProperty(string.Concat("Item",i.ToString())))
                .Select(pi => pi.GetGetMethod())
                .Select(getter => Delegate.CreateDelegate(
                            typeof(Func<>).MakeGenericType(getter.ReturnType), 
                            this.Key, 
                            getter))
                .ToList();
    }

    public int TupleRank {get; private set;}
    public IEnumerable<Type> TupleSubtypes {get; private set;}
    public IList<Delegate> TupleGetters {get; private set;}
    public TKey Key {get; private set;}

    public object this[int rank]
    {
        get { return TupleGetters[rank].DynamicInvoke(null);}
    }
    public void DoSomethingUseful()
    {
        for(int i=0; i<TupleRank; i++)
        {
            Console.WriteLine("Key value for {0}:{1}", string.Concat("Item", i+1), this[i]);
        }
    }
}

Test rig:

var classTup1 = new Class1<Tuple<int>>(Tuple.Create(1));
var classTup2 = new Class1<Tuple<int,int>>(Tuple.Create(1,2));
var classTup3 = new Class1<Tuple<int,int,int>>(Tuple.Create(1,2,3));
var classTup4 = new Class1<Tuple<int,int,int,int>>(Tuple.Create(1,2,3,4));
var classTup5 = new Class1<Tuple<int,int,int,int,int>>(Tuple.Create(1,2,3,4,5));

classTup1.DoSomethingUseful();
classTup2.DoSomethingUseful();
classTup3.DoSomethingUseful();
classTup4.DoSomethingUseful();
classTup5.DoSomethingUseful();

Output:

Key type is a Tuple (I think) with 1 elements
Key type is a Tuple (I think) with 2 elements
Key type is a Tuple (I think) with 3 elements
Key type is a Tuple (I think) with 4 elements
Key type is a Tuple (I think) with 5 elements
Key value for Item1:1
Key value for Item1:1
Key value for Item2:2
Key value for Item1:1
Key value for Item2:2
Key value for Item3:3
Key value for Item1:1
Key value for Item2:2
Key value for Item3:3
Key value for Item4:4
Key value for Item1:1
Key value for Item2:2
Key value for Item3:3
Key value for Item4:4
Key value for Item5:5

Problem

I'd like do create a class with one generic TKey, where TKey is one of System.Tuple types that can be created. ``` public class Class1<TKey> where TKey : System.Tuple { /// Class Stuff Goes Here where TKey is one of the 8 tuple types found in the link in (1) } ``` I am not so sure how to implement this. The goal is to prevent myself from implementing a class for each tuple class.

Original source