tags:

views:

729

answers:

7

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)

+6  A: 

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() });
Mark Byers
Anonymous types can't be passed outside of the methods that create them so they're considerably less useful in that regard.
Rory
Yes, this is true. They're certainly no replacement for a rich man's Tuple, but it's a useful trick in many situations when you only need the "tuple" inside a single scope.
Mark Byers
@Rory: that isn't entirely true... http://msmvps.com/blogs/jon_skeet/archive/2009/01/09/horrible-grotty-hack-returning-an-anonymous-type-instance.aspx ... but it will only work in the same assembly. Also you can use reflection tricks (see the ASP.Net MVC framework)
Matthew Whited
A: 

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

Henri
A: 

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.

Reed Copsey
A: 
  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).
Rory
A: 

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

Bill K
+3  A: 

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
}
Matt Davis
A: 

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.

The code that inspired this post gets a lot of mileage out of the Tuple without an unpacking equivalent (see: http://blogs.msdn.com/lucabol/archive/2009/09/22/becoming-really-rich-with-c.aspx ) - It could be argued that it's code in the "rough" but Bolognese passes tuples around and uses LINQ expressions and generic properties to do the reading of individual values.
David in Dakota
I'm a big fan of Luca and I'm certain he has had great success with his tuple types. I was merely pointing out that if you strech your imagination and challenge yourself, sometimes you can pull off what seems impossible. For example, what if Luca's code:var ticker = tuple.Item1;var name = tuple.Item2;var asset = tuple.Item3;var subAsset = tuple.Item4;could be written as suchvar (ticker, name, asset, subAsset) = tuple;That seems impossible... like C# would require a language update to support it. Maybe people thought the same with C++ but someone got it figured out.