Coffeehouse Thread

32 posts

How are you using Tuple in code?

Back to Forum: Coffeehouse
  • User profile image
    Dr Herbie

    Because I'm in the office a little early and there's no one else to distract me, I've been analysing my coding style.

    I have decided that the Tuple<> class is ugly (having to remember which type is 'Item1' and which is 'Item2') and that it should only be used within a class and never used as a result passed out of a public class method or property; I think it's OK to use within a class's private methods, but it's a bit of a 'sausage factory' argument.

    So do you use Tuple<> at all?

     

    Herbie

  • User profile image
    Ian2

    Shoot me down (again) as I hadn't fully embraced the Tuple up to this point in time.

    But now tat I have refreshed my memory I could actually see me using it quite a lot.  Like many others I find less and less use for databases WHAT!!!!!!  (YES, especially in smaller apps) and this looks like something I should have already been making more use of.  We will see.

     

    Thanks for the nudge,

  • User profile image
    evildictait​or

    The only time I ever use it is as a generic argument, where I'd really like two things instead of one generic argument.

    For example, a List<Tuple<string, int>> or an GenericEventArgs<Tuple<string, TcpClient>> or some such.

  • User profile image
    vesuvius

    @Dr Herbie: Yes I do use it sometimes, and comments in the code and intellisense help with the "item1" issue. I use Tuple.Create a lot as well, there are legitimate use cases, and none of my other developers complain too much either

  • User profile image
    vesuvius

    , evildictait​or wrote

    The only time I ever use it is as a generic argument, where I'd really like two things instead of one generic argument.

    For example, a List<Tuple<string, int>> or an GenericEventArgs<Tuple<string, TcpClient>> or some such.

    I have used it when I needed something elegant like this http://stackoverflow.com/questions/4251183/wpf-two-dimensional-datagrid-listview

  • User profile image
    wkempf

    I find Tuple less useful in OO languages that don't include "tuple assignment" and therefor generally prefer domain types over the non-specific Tuple. Give me tuple assignment, though, and I'd probably use it a lot in C#.

    int index, string name = FindMostExpensiveProduct(); // dumb example

  • User profile image
    Garulon

    @wkempf:the function signature

    Tuple<int,string> FindMostExpensiveProduct() would do that.

     

  • User profile image
    wkempf

    @Garulon: Not in C# it wouldn't. C# doesn't have tuple assignment. In C# you'd have:

    Tuple<int, string> tuple = FindMostExpensiveProduct();

    Which is not even close to the same thing.

  • User profile image
    spivonious

    Readability suffers with Tuple.

    Product p = FindMostExpensiveProduct();

    or

    Tuple<int, string> product = FindMostExpensiveProduct();

  • User profile image
    Dr Herbie

    @spivonious: That's my worry -- which is why I only use it in calls to private methods.

    Herbie

  • User profile image
    wkempf

    @spivonious: That's part of the benefit of tuple assignment. Concsumption isn't helped, as you have to know the what the items in the tuple are when writing the code, but reading the code has been brought back into parity (or, arguably, made even better).

    int index, string name = FindMostExpensiveProduct();

    Given that, I know FindMostExpensiveProduct() just returned me an index and a name. In your example:

    Product p = FindMostExpensiveProduct();

    I'd assume I was given back a product, which would include a lot more than just an index and a name. This discussion would be easier if I'd picked a better example, as this particular API is extremely dumb, whether you use a Tuple or not.

    Honestly, though, Tuple still suffers in an OO language. Most of the time where you'd care to use a Tuple an anonymous type or a domain specific type would work out better. I just know that in languages that support tuple assignment I've never had problems with using tuple types, while in C# I've never written anything but throw away code that used one of the Tuple types.

  • User profile image
    cbae

    , evildictait​or wrote

    The only time I ever use it is as a generic argument, where I'd really like two things instead of one generic argument.

    For example, a List<Tuple<string, int>> or an GenericEventArgs<Tuple<string, TcpClient>> or some such.

    Instead of List<Tuple<string,int>>, I'd probably use Dictionary<string,int> if the string element needed to be unique.

    I would most likely use Tuple<int,int> for managing data for 2D graphing. The benefit is that it's a reference type, whereas something like System.Drawing.Point is a value type, and passing the Tuple object as an argument to methods is more efficient.

    If you're doing 3D surface modelling, then Tuple<int, int, int> is the only way to go.

     

  • User profile image
    Bass

    , cbae wrote

    *snip*

    Instead of List<Tuple<string,int>>, I'd probably use Dictionary<string,int> if the string element needed to be unique.

    I would most likely use Tuple<int,int> for managing data for 2D graphing. The benefit is that it's a reference type, whereas something like System.Drawing.Point is a value type, and passing the Tuple object as an argument to methods is more efficient.

    If you're doing 3D surface modelling, then Tuple<int, int, int> is the only way to go.

     

    You can pass value types by reference in C# using the "out" keyword. If your reason for doing this is performance, I'd strongly suggest benchmarking first.. that's a pretty controversial assumption you are making about the performance of reference types, especially when going against value types that are 8 or 12 bytes.

    Also I would recommend capital punishment for anyone who uses "Tuple" as a parameter argument in an API. Seems appropriate for the crime.

  • User profile image
    cbae

    , Bass wrote

    *snip*

    You can pass value types by reference in C# using the "out" keyword.

    If your reason for doing this is performance, I'd strongly suggest benchmarking first.. that's a pretty controversial assumption you are making about the performance of reference types, especially when going against value types that are 8 or 12 bytes.

    If you're holding instances of the type in a collection, you'd still have to make a local copy when enumerating before you can pass the instance by reference to a method.

    Also I would recommend capital punishment for anyone who uses "Tuple" as a parameter argument in an API. Seems appropriate for the crime.

    Why? void PlotIt(Tuple<int, int, int> xyzCoord) is a self-explanatory method signature. You wouldn't even need to add any comments.

  • User profile image
    exoteric

    , Dr Herbie wrote

    Because I'm in the office a little early and there's no one else to distract me, I've been analysing my coding style.

    I have decided that the Tuple<> class is ugly (having to remember which type is 'Item1' and which is 'Item2') and that it should only be used within a class and never used as a result passed out of a public class method or property; I think it's OK to use within a class's private methods, but it's a bit of a 'sausage factory' argument.

    So do you use Tuple<> at all?

    Herbie

    Not that much. Tuples can be used to model fixed-length, statically-typed, heterogeneous collections. If the tuple is large it becomes hostile to use in my oppinion: like a long function parameter list; this is, I believe, the reason why named arguments were introduced in C#.

    In F# they look syntactically nicer but F# also has immutable record types which in my oppinion are much, much better since they are self-documenting - unlike tuples. Tuples are kind of everywhere anyway though, as argument lists and also in discriminated union constructors.

    For simple things like coordinate sets tuples can be fine (although one wonders what their performance will be with Tuple being implemented as a reference type).

    One place where I could maybe see using a tuple could be e.g. a list search function which returns both the index of an element as well as the element itself. However, given a choice, the record type would almost always be better, in my oppinion. Also, come to think of it if the element type is the same as the index type it is confusing again, without resorting to the documentation. Record types eliminate all confusion.

    The question is whether the syntactic overhead of the object literal makes the code harder to easier to read. If a tuple is used locally in a function for some purpose but is not passed around then it can also be appropriate but then again, one could also just use two or more local variables for that. The more I think of them, the less I like tuples. They are often ambiguous constructs.

  • User profile image
    cbae

    , exoteric wrote

    *snip*

    For simple things like coordinate sets tuples can be fine (although one wonders what their performance will be with Tuple being implemented as a reference type).

    Inefficient during allocation and efficient when passed as arguments. Always a trade-off.

    The question is whether the syntactic overhead of the object literal makes the code harder to easier to read. If a tuple is used locally in a function for some purpose but is not passed around then it can also be appropriate but then again, one could also just use two or more local variables for that. The more I think of them, the less I like tuples. They are often ambiguous constructs.

    If the objects in question aren't going to be passed around, anonymous types are far better at self-documenting.

  • User profile image
    BitFlipper

    I had a case where I thought tuples would work great. I needed to get the current performance statistics from a running operation (RunCount, AvgTime, MaxTime, MinTime, MeanTime, etc). The problem with this is that you need to lock() the call where the values are calculated in order to make sure it thread safe. So if I just had properties for RunCount, MaxTime etc then you can get values are are out of sync with each other.

    Just like others have noted, the problem is that you don't know which value is which, and it is easy to get them mixed up, especially if some of them are of the same type (was AvgTime before MaxTime?). After trying it for a while I just gave up and return the statistics in a dedicated structure.

    I though a solution would have been if you could have declared the tuple like this:

    public class Operation
    {
        public Tuple<int RunCount, TimeSpan MaxTime, TimeSpan MinTime, TimeSpan AvgTime> Statistics
        {
            get
            {
                lock (m_lock)
                {
                    var tuple = new Tuple<int RunCount, TimeSpan MaxTime, TimeSpan MinTime, TimeSpan AvgTime>();
                    tuple.RunCount = m_runs;
                    ///etc...
                    return tuple;
                }
            }
        }
    }
    
    // ...Then...
    
    var stats = operation.Statistics;
    
    runCountLabel.Text = stats.RunCount.ToString();
    avgTimeLabel.Text = stats.AvgTime.ToString();
    //...etc

    It gets a bit verbose but then again it saves having to declare a dedicated structure ahead of time.

  • User profile image
    SteveRichter

    [quote]

    , Dr Herbie wrote

    I have decided that the Tuple<> class is ugly (having to remember which type is 'Item1' and which is 'Item2') and that it should only be used within a class and never used as a result passed out of a public class method or property;

     /quote]

    how else do you return multiple values from a method? out arguments are a bit clunky in that they require the "out" modifier and all the out arguments have to be declared and specified on the call. Declaring a class to be returned is probably ideal, but takes time to code and is another class you have to add to the project.

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.