Il .Net Framework 2.0 non dispone di un tipo "tupla", inteso come una sequenza finita (non necessariamente omogenea) di valori di un certo tipo. Un tipo del genere potrebbe essere utilizzato in molti contesti: quando devo rappresentare coppie (triple, quadruple, ..., n-uple) di valori, quando un metodo deve tornare più valori e non posso o non voglio utilizzare i parametri di tipo out, ...
Grazie all'utilizzo dei Generics e con un po' di overload è possibile implemetare un tipo Tuple che faccia al caso.
Es.

public interface ITuple : IEnumerable, IComparable<ITuple>
{
  
int Length
  
{ get; }

   object this[int index]
  
{ get; set; }
}

// Tupla vuota. Es. ()
public
struct Tuple : ITuple
{
  
public int Length
  
{
     
get { return 0; }
  
}

   public object this[int index]
  
{
     
get { throw new ArgumentOutOfRangeException("index"); }

      set { throw new ArgumentOutOfRangeException("index"); }
   }

   // ...
}

// Tupla di un elemento di un certo tipo. Es. (10), ("Hello world"), ...
public
struct Tuple<T0> : ITuple
{
  
public T0 Item0;

   public Tuple(T0 item0)
  
{
     
this.Item0 = item0;
  
}

   public int Length
  
{
     
get { return 1; }
  
}

   public object this[int index]
  
{
     
get
     
{
        
switch (index)
        
{
           
case 0:
              
return Item0;
           
default:
              
throw new ArgumentOutOfRangeException("index");
        
}
     
}

      set
     
{
        
switch (index)
         
{
           
case 0:
              
Item0 = (T0)value;
              
break;
           
default:
              
throw new ArgumentOutOfRangeException("index");
        
}
      
}
  
}
}

public struct Tuple<T0, T1> : ITuple /* ... */ }

public struct Tuple<T0, T1, T2> : ITuple /* ... */ }

public struct Tuple<T0, T1, T2, T3> : ITuple /* ... */ }

Il codice descritto permette di costruire tuple che hanno al massimo 4 elementi, non è chiaramente possibile dichiarare un tipo Tuple con un numero indefinito di type parameter... Il codice, al variare del numero dei parametri, è sempre lo stesso. Si potrebbe evitare di scrivere troppo codice avendo una classe base Tuple; la scelta di implementare come struct è per prevenire ipotetici problemi di prestazioni vista la probabilità di dover costruire collection di tuple. Vediamo un possibile utilizzo:

Tuple<int, string> t = new Tuple<int, string>(10, "Hello");
Console.WriteLine(t.Item0);  // 10
Console.WriteLine(t[1]);     // "Hello"

Vediamo ora la costruzione di una tupla di tuple:

Tuple<Tuple<int, int>, Tuple<string, string>> t4 = new Tuple<Tuple<int, int>, Tuple<string, string>>(new Tuple<int, int>(10, 20), new Tuple<string, string>("Hello", "world"));  // ((10, 20), ("Hello", "world"))

La costruzione di tuple può essere semplificata dal punto di vista sintattico sfruttando l'inferenza dei type parameter sui metodi:

public class Tuple
{
   // ...

   public
static Tuple
New()
   {
     
return new Tuple();
   }

   public static Tuple<T0> New<T0>(T0 item0)
  
{
     
return new Tuple<T0>(item0);
  
}

   public static Tuple<T0, T1> New<T0, T1>(T0 item0, T1 item1)
  
{
     
return new Tuple<T0, T1>(item0, item1);
  
}

   // ...
}

A questo punto il precedente esempio può essere riscritto (in modo più semplice) come:

Tuple<Tuple<int, int>, Tuple<string, string>> t4 = Tuple.New(Tuple.New(10, 20), Tuple.New("Hello", "world"));

o anche

ITuple t4 = Tuple.New(Tuple.New(10, 20), Tuple.New("Hello", "world"));

Con un po' di ingegno è possibile aggiungere altre feature al tipo Tuple: overload degli operatori di uguaglianza e diseguaglianza, overload di ToString(), enumerazione con foreach degli elementi della tupla, ...