When to use: Tuple vs Class in C# 7.0
Asked Answered
A

11

101

Before Tuples, I used to create a class and its variables, then create object from this class and make that object the return type for some functions.

Now, with tuples, I can do the same thing, and in C# 7.0 we can assign understandable names for tuple properties (before this, it was item1, item2, etc..)

So now I am wondering, when do I use tuple and when do I create a class in C# 7.0?

Arawak answered 20/6, 2017 at 10:33 Comment(5)
Note that the tuple element names are mostly a design time property and do not exist in the compiled IL. So if you care about that, you have another reason for proper classes.Bulgaria
@OrkhanAlikhanov While that would be a correct duplicate target, I do agree with OP that the times have changed now with C# 7, so answers from 2012 and 2010 do only partially apply now.Bulgaria
@Bulgaria that is not a valid duplicate target, that question was about System.Tuple, this one is about the new System.ValueTuple which has special syntactic sugar in C# 7.Lilywhite
@Lilywhite You do realize that I was criticizing the suggestion to close this question, right? I know what this question is about which is why I suggested to leave this open.Bulgaria
@Lilywhite Oh, I just made the same mistake in interpreting this question as referring to System.Tuple, since it says… "Tuple". If you're confident it's actually referring to something else, then you should edit it to make that more clear. I reverted my closure assuming you are correct.Tousle
D
78

As this answer is causing some confusion amongst some folk here, I should clarify that - as per the question - all references to "tuple" here refer to the ValueTuple type and new tuple syntactic sugar features of C# 7 and in no way refer to the old System.Tuple reference types.

So now I am wondering, when Should I use tuples and when Should I create a class in c# 7.0?

Only you can really answer that question as it really depends on your code.

However, there are guidelines and rules you can follow in guiding you in choosing between them:

Tuples are values, so are copied by value, rather than by reference.

Most of the time, this should not be an issue. However, if you are passing around tuples of large structs, this might have an impact on performance. Ref locals/returns can be used to work around these performance issues, though.

Additionally, because they are values, modifying a copy remotely will not change the original copy. This is a good thing, but could catch some folk out.

Tuple element names are not persisted

The names given to elements are used by the compiler and (in most cases) are not available at run-time. This means that reflection cannot be used to discover their names; they cannot be accessed dynamically and they cannot be used in razor views.

Also this is an important consideration with APIs. A tuple returned from a method is the exception to the rule regarding after-compilation name discoverability. The compiler adds attributes to the method that hold information on the tuple names. This means you can safely return a tuple from a public method in one assembly and access its names in another.

Tuples are lightweight

Tuples are much simpler to write than types as they are less verbose and the declaration can be "inlined" (ie declared at the point of use). This works well when declaring a method that returns multiple values, for example.

However, because they are declared at the point of use, if you have MethodA that calls MethodB that calls MethodC and each returns a tuple, you'll need to redefine the tuple at every stage. There isn't (yet) a way of creating an alias of a tuple and re-using it across multiple methods.

Just use common sense

For any situation where you might consider using a tuple: simply ask yourself the question: "will a tuple simplify the code here". If the answer is "yes", then use one. And that ultimately is the primary consideration over whether to use a tuple or a custom class.

Dejection answered 20/6, 2017 at 11:40 Comment(6)
ValueTuple is value type. Tupleis reference type. So simple Tuple wont be copied by reference. You should mention it in bold because it is confusing right nowGlaser
@Szer, the question is about C# 7's tuples, so I don't see why there's any confusion. But I've updated the question to clarify that point.Dejection
@DavidArno when I first started using C#7 it was not clear to me if the new Tuple syntax was a new type or just language support.Yonder
I am surprised that tuple element names couldn't be used in Razor views. Aren't Razor files converted to C# and then compiled? If so, they should see the tuple element names from metadata/IL, just like any client compilation does.Sorority
@JulienCouvreur, as far as I know, it's a limitation of the razor tooling. However, by the looks of github.com/aspnet/Razor/issues/1046, this is now either fixed or a fix will be released soon.Dejection
Thanks for the link. It looks like no fix was necessary (they just pulled in a new nuget package). I will follow up with the ASP.NET Core team to understand and smooth the compiler adoption story. CheersSorority
Y
36

Generally speaking, named classes have some significance in the design of your system. They are also more verbose to write. For example, you may have a class called MediaFileOpener. It is important to the design that we know what this class does - we are working with media files!

Anonymous types and tuples are used when there is no design significance and all you want is a lightweight Data Transfer Object (DTO) to move information around.

As rule, if your class requires some documentation to describe what it is for, or if there is behaviour that it provides, use a full class. If all you need is temporary storage or some sort of grouping, use a Tuple. Consider a situation where you want to return more than one value from an async method. Tuple is designed to solve that problem.

Yonder answered 20/6, 2017 at 10:54 Comment(0)
M
13

Use a Class

If your objects are entities that are widely used throughout your application and are also stored in some kind of persistent storage like a relational database (SQL Server, MySQL, SQLite), a NoSQL Database or Cache (Redis, Azure DocumentDB) or even on simple text files or CSVs.

So yeah, anything persistent should have its own class.

Use a Tuple

If your objects are short lived without a special meaning for your application. For example, if you need to quickly return a pair of coordinates it's better to have something like this:

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}

than define a separate class

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

A Tuple will save you time from having to allocate memory on the heap using new for such a simple operation.

Another time when I find tuples useful is when performing multiple mathematical operations on some operands

(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)

It doesn't make sense to define a class in this case. No matter what the calculations are the results do not belong to a class. So the alternative would be to use out variables but I believe tuples are more expressive and result in improved readability.

Mehetabel answered 20/6, 2017 at 10:59 Comment(3)
I would define Coordinates as struct instead of class.Marthena
Coordinates would be a good example for not using a tuple since it’s a common enough thing that you likely need it all the time. In addition, you can overload ToString to provide a readable output (incidentally, the tuple’s default ToString already works well here), and overload Equals/GetHashCode for equality comparisons too.Bulgaria
IMHO, this seems like a very opinionated response, with a vague oop desgin guideline. I don't think that oop has anything to do with this (anonymous classes can be put into both of your categories). Also, adding database level persistence has nothing to do with these considerations.Shanney
R
7

Generally you want to have a class when your object is going to be used somewhere else or if it represents a real object or a concept in your domain. You're probably going to create a class to represent a car or a car store, not tuples.

On the other hand, sometimes you just want to return a couple of objects from a method. Maybe they don't represent anything special, it's just that you need to return them together in that particular method. Sometimes, even if they do represent a concept from your domain (say you're returning (Car, Store), which could be represented as a Sale object), you're not actually going to use them anywhere -- you're just moving data. In those cases, it's fine to use tuples.

Now, speaking about C# specifically, there's one more thing you should know. C# 7's tuple type is actually the ValueTuple, which is a struct. Differently from classes, which are reference types, structs are value types. You can read more about that on msdn. Most importantly, know that they might involve a lot of copying, so be careful.

Revision answered 20/6, 2017 at 11:0 Comment(0)
L
6

I want to start by mentioning that C# already had support for anonymous types. Which are reference types. Thus, you already had a suitable alternative to creating a named class.

One advantage of a named class is the will be easier to reuse (for example if you need the same type in multiple places) and document. Since the anonymous type is anonymous, you can only get variables typed to it if you can use var, which limits the contexts in which anonymous types are useful (for example, you can't use them as field types, return types or parameter types).

Of course, you could go over some of the limitations of anonymous types by using System.Tuple. Which is also a reference type, and you can use it explicitly. The downside is that it lacks custom names for the members.


C# 7 tuples (ValueTuple) can be considered similar to anonymous types. The first difference is that they are value types. This means that these tuples will have performance advantage as long as they stay in local scope or are moving across the stack (which is the common usage of anonymous types, due to its limitation).

The second difference is that the new syntax allows tuples to appear in more places than anonymous types, as you know, you have syntactic sugar to define the return type to ValueTuple (while using anonymous types you had to return object).

The third difference is that ValueTuple supports deconstruction out of the box. To quote What’s New in C# 7.0:

Another way to consume tuples is to deconstruct them. A deconstructing declaration is a syntax for splitting a tuple (or other value) into its parts and assigning those parts individually to fresh variables:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

Which you can also do with custom types by adding a Deconstruct method.


For abstract:

  • ValueTuple has syntactic sugar in C# 7.0, which should be considered for readability.
  • ValueTuple is a value type. All pros and cons between the use of a class and a struct apply.
  • ValueTuple can used explictly (with or without the syntactic sugar) allowing it to have the versatility of System.Tuple while retaining named members.
  • ValueTuple supports deconstruction.

Given that must of it is syntactic sugar, I would say that the stronger argument to choose ValueTuple is the same argument to choose a struct. Which would be ideal for small, immutable types, that live mostly on the stack (so you do not have lots of boxing and unboxing).

Comparing ValueTuple against a full blown struct, considering the syntactic sugar, I would suggest to use ValueTuple by default, unless you need an explicit layout or you need to add methods to it.

I also want to say that the syntactic sugar does not necessarily improve readability. The main reason being that you are not naming the type, and the name of the type provides meaning to the code. In addition of that, you can add documentation to a struct or class declaration that eases understanding.

All in all, the situation where ValueTuple really shines is returning multiple values from a method. In this case removing the need of creating new out parameters. And the documentation of the ValueTuple used can live in the documentation of the method. If you find you need to do something else with the ValueTuple (defining extension methods for example), I would suggest to consider creating a named type instead.

Lilywhite answered 20/6, 2017 at 11:36 Comment(0)
G
5

I think this is going to become a question that gets asked a lot. There's currently no "best practice" for when to use the new value tuples vs a class.

However, it's worth reading what came up during previous conversations about the previous versions of tuples vs a class.

In my opinion the value tuple should only be used minimally and with no more than a maximum of three values. I think this strikes a good balance of "returning some values without the need for a class" and "horrific mess of values". If you have more than three values to return, make a class.

I'd also never use a tuple to return from a public facing API that consumers will have to use. Again, just use a class.

Here's some real world code that I've used:

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

As soon as I want to return more complex data, I make a class.

Grendel answered 20/6, 2017 at 11:1 Comment(0)
P
3

I would avoid using tuples as a return-type of public methods. In such cases I'd prefer to define a class or struct.

Pugh answered 23/1, 2018 at 13:16 Comment(4)
Could you explain why?Rondeau
@Fütemire Google "missing abstraction" or "primitive obsession" design smell.Rare
@MilosMrdovic thanks, I'll look into that.Rondeau
Strong agreement. Named tuples provide a weak domain definition for your library's public interface. I suppose it's fine if you hide them in your internals for a one-off scenario, but anything exposed publicly is, by definition, expected to get used. Define a domain-appropriate abstraction!Semicolon
A
2

Tuples are intended to represent multiple values, like when a method intends to return multiple values. The tuple support in C# 7 uses System.ValueTuple<...> instances to represent that set of values. The names of those values are valid only in the context where they are being used and are not enforced.

Classes are intended to represent a single value with several attributes.

Assagai answered 20/6, 2017 at 10:58 Comment(0)
G
2

Tuple is a great option when you want to combine multiple values (can be different types) into one object without creating a custom class. In this case, Tuple would be a fast and a perfect option to go with.

Goulet answered 15/3, 2018 at 10:43 Comment(0)
P
2

I recently compared performance difference for a case I had involving either a tuple containing 2 (or 4) primitives and a class containing 2 (or 4) of the same primitives types. I'm sharing here in case that can help anyone.

Profiling

Proprietor answered 26/3, 2021 at 9:33 Comment(3)
What was the outcome?Crossfertilize
@GrahamLaight As per the screenshot, using a Tuple<T1, T2, TN> is significantly faster than using a class with the same set of field types, both for initialisation and for field access. Additionally, the higher the number of fields, the bigger the difference.Premillenarian
@MickaelBergeronNéron Please consider adding the text of your findings to your answer. I think that it would really be helpful and make the answer a lot more digestible.Janicejanicki
C
2

For a quick piece of code that's only going to be used once, use tuples. If the code is going to have to be maintained, use classes. It's demoralising to look at code and see expressions like:

if (t.Item4 == x.Item3)
Crossfertilize answered 14/7, 2021 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.