Question

I find myself occasionally in C# 3.0 looking for ways to simulate the notion of a tuple. Over time I've had various "poor man's" implementations, here are a few of them:

Basic Object Array:

object[] poorTuple = new object[]{foo,bar,baz}; // basic object array

More Strongly Typed, HoHoHo...

KeyValuePair<TypeA, KeyValuePair<TypeB, TypeC>> poorTuple;

Implementing a class that can use type inference (lifted from Functional Programming for the Real World)

public static class Tuple{
  public static Tuple<T1, T2> Create<T1 foo, T2 bar>{
    return new Tuple<T1, T2>(foo, bar);
  }
}
// later: 
var data = Tuple.Create("foo", 42);

Questions:

  1. Any other ways to have a poor man's tuple in C# 3.0 (or language of choice that lacks the data structure).

  2. What is the best way to get a tuple in C# 3.0 - if anyone has a library recommendation it is welcome.

  3. At what point (yes, generalize for me) does it make sense to create a specific type rather than something like a list or tuple? (looking for rules of thumb)

Was it helpful?

Solution

You can create anonymous types which function similarly to tuples, except with useful names:

var itemsWithChildCounts 
    = myObjects.Select(x => new { Name = x.Name, Count = x.Children.Count() });

OTHER TIPS

Here is the code for a generic tuple taken from Bill Wagner's article in the April 2007 edition of Visual Studio Magazine.

public struct Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
{
    private readonly T1 first;
    public T1 First
    {
        get { return first; }
    }

    private readonly T2 second;
    public T2 Second
    {
        get { return second; }
    } 

    public Tuple(T1 f, T2 s)
    {
        first = f;
        second = s;
    }

    #region IEquatable<Tuple<T1,T2>> Members
    public bool Equals(Tuple<T1, T2> other)
    {
        return first.Equals(other.first) && 
            second.Equals(other.second);
    }

    public override bool Equals(object obj)
    {
        if (obj is Tuple<T1, T2>)
            return this.Equals((Tuple<T1, T2>)obj);
        else
            return false;
    }

    public override int GetHashCode()
    {
        return first.GetHashCode() ˆ second.GetHashCode();
    }
    #endregion
}

For 1 - 2: I prefer implementing your own tuple class. The implementation you stole is a decent one. It should work well.

For 3: Here's my rule of thumb - As soon as you're going to reuse this functionality in multiple methods (with the same types having the same meaning), or if you use it in any public API, I think it's time to implement a "real" class with your specific types.

  1. The "Implementing a class" method is as good as it's going to get (and it should be in the next version of .NET anyway)
  2. No idea, I keep them in my own personal library of classes for now.
  3. I'd consider creating a proper class for them as soon as they get exposed to the public (i.e. when they are no longer used just to store related values for a class internally).

Since you asked for an opinion, mine would be to always create a type--I can't figure out a reason not to.

More often than not you can find that you actually needed the type (the main use is either to store two items in a collection or return two items from a method call--in both cases if the items aren't closely related, you're probably doing something wrong).

I think it's good to create a new type when it makes sense to introduce a new value set into your program. For example, if you are writing a complex calculator, then make a complex number type. If you really just want to "glue" a few variables together for a moment, then a tuple is probably a better choice. Let's say you've got a simple function that gathers two numbers from the console... you might do something like:

static void GetNumbers(out int x, out int y) { ... }
...
int x, y;
GetNumbers(out x, out y);

I think it usually makes sense when a "getter" function has a return value but in this case it doesn't because I can't really have two return values. I could go and make a new type called TwoNumbers and use that but I think this quickly becomes more of a problem than a solution. If C# had tuples I may be able to do something like the following:

static (int, int) GetNumbers() { ... }
...
int x, y;
(x, y) = GetNumbers();

Now the really interesting question is: although C# doesn't have this feature am I able to implement it myself with a library? The code you suggest is a start but won't allow me to assign like I've done in the second example. I'm not sure about C# but you can get really darn close to this in C++ by using the fact that a function call can be the left operand to the assignment operator when it's return value is a reference type. Consider the following:

// this class is implemented much like yours only with C++
template<typename T1, typename T2> class tuple { ... }
...
template<typename T1, typename T2> tuple<T1, T2>& tie(T1 a, T2 b) { ... }
...
template<typename T1, typename T2> tuple<T1, T2> get_numbers() { ... }
...
int x, y;
tie(x, y) = get_numbers();

I'd say that is pretty darn close... instead of (x, y) = we have tie(x, y) =. If you are interested in implementation details, I'll refer you to the TR1 library where I first learned about this:

http://www.boost.org/doc/libs/1_41_0/libs/tuple/doc/tuple_users_guide.html#tiers

So to bring all this back to C# 3.0... we can certainly create a generic tuple class as you've shown, but can we create a function like tie to "unpack" the tuple in C#? I haven't tried it, sounds like fun. Without something like that, I'd be reluctant to let a C# tuple library proliferate through my code.

I dont like type inference in C# since it is abused to much for shorter writing. Of course it is a very cool feature, but people abuse it IMHO like in this case.

In this case i would specify the type explicitly to avoid confusions about the type (i.e. perhaps 42 is a long, or a byte or a short).

So why not having a simple tuple class, which can be implemented in just a few lines. And if you're lazy you can even write some extension methods for your tuple class. This makes life easier but also cleaner.

Dont see the point of having a "fancy" tuple class instead of the generic one you presented (except for the type inference).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top